Xcode UI tests, development language, fallback translation and CI servers

465 Views Asked by At

Following scenario:

I'm having an iOS project that automatically gets unit and UI tested on every git push on a CI server (CircleCI). The tests are executed with fastlane which is also used to create screenshots for the App Store automatically.

Now, my project gets translated into several languages. I want fastlane to work on all languages (to be able to take the screenshots), so I changed the UI tests from something like this:

app.navigationBars.buttons["Confirm"].tap()

to

let buttonTitle = NSLocalizedString("navbar.confirm", comment: "")   
app.navigationBars.buttons[buttonTitle].tap()

I thought this will do the trick, but it doesn't. I don't know how the simulator is configured in CircleCI, but the UI tests now fail with

[00:46:34]: ▸ testDashboard, No matches found for Find: Elements matching predicate '"navbar.confirm" IN identifiers' from input {(

So for some reason the fallback language set with CFBundleDevelopmentRegion is not respected, probably because the language is not in the preferredLanguages list of the bundle. This is an issue in itself as I do not want keys to be displayed for end users in any case. I'd like to make sure this never ever happens.

So I tried to fix that in turn by writing a wrapper for NSLocalizedString that checks whether NSLocalizedString(..) returns the key and if so loads the default (en) bundle and gets the string localized that way.

However it seems you can't load another bundle in the UI tests. The test will crash and fail. So I can't use this workaround.

Am I just overlooking some obvious solution? I can't be the only one having this issue, right? Any hints?

2

There are 2 best solutions below

1
On BEST ANSWER

Set accessibility identifiers on your UI elements and query using those in your tests instead of using the localized string values. This will make your tests language-agnostic and there will be no need to fiddle with access to different bundles.

See this answer for an example of how to set and use accessibility identifiers.

0
On

When you use NSLocalizedString("navbar.confirm", comment: "") the system tries to retrieve a value for that key from the strings file in the main bundle.

The main bundle does not work when running UITests because it gives you the bundle of the UITest Runner App instead of the UITest bundle.

To be able to use NSLocalizedString in a UITest you have to do 2 things:

1. Add your Localizable.strings file to your UITest target

2. Access the file via the UITest bundle (You can use a little helper method for that):

func localized(_ key: String) -> String {
    let uiTestBundle = Bundle(for: AClassFromYourUITests.self)
    return NSLocalizedString(key, bundle: uiTestBundle, comment: "")
}

You can use any class from your UITests to access the UITest bundle.

Now you can use NSLocalizedString in your UITest like this:

app.navigationBars.buttons[localized("navbar.confirm")].tap()

I wrote a little blog post about this a while ago, if you are interested in more details.