Xcode 7 Uitesty ze zlokalizowanym interfejsem użytkownika

W mojej aplikacji używam NSLocalizedString, aby zlokalizować moją aplikację. Teraz chcę przełączyć się na UITests i habe Testcode Tak:

[tabBarsQuery.buttons["particiants"] tap];

To działa w języku Angielskim, ale zawodzi w innych językach.

[tabBarsQuery.buttons[NSLocalizedString("PARTICIPANTS",comment:nil)] tap];

Fails-prawdopodobnie dlatego, że Localizable.struny są w innym pakiecie. Jak Mogę przetestować zlokalizowaną aplikację?

Author: svmrajesh, 2015-11-09

9 answers

Chciałem przetestować zawartość funkcji interfejsu użytkownika, a nie tylko ich istnienie, więc ustawienie domyślnego języka lub użycie identyfikatorów dostępności Nie pasowałoby.

To buduje się na odpowiedziach Wołodymyra i matsoftware. Jednak ich odpowiedzi opierają się na deviceLanguage, która musi być jawnie ustawiona w SnapshotHelper. To rozwiązanie dynamicznie pobiera aktualnie obsługiwany język, którego używa urządzenie.

  1. Dodaj pliki Localizable.strings do swojego Uitestu cel.
  2. Dodaj następujący kod do celu UITest:

    var currentLanguage: (langCode: String, localeCode: String)? {
        let currentLocale = Locale(identifier: Locale.preferredLanguages.first!)
        guard let langCode = currentLocale.languageCode else {
            return nil
        }
        var localeCode = langCode
        if let scriptCode = currentLocale.scriptCode {
            localeCode = "\(langCode)-\(scriptCode)"
        } else if let regionCode = currentLocale.regionCode {
            localeCode = "\(langCode)-\(regionCode)"
        }
        return (langCode, localeCode)
    }
    
    func localizedString(_ key: String) -> String {
        let testBundle = Bundle(for: /* a class in your test bundle */.self)
        if let currentLanguage = currentLanguage,
            let testBundlePath = testBundle.path(forResource: currentLanguage.localeCode, ofType: "lproj") ?? testBundle.path(forResource: currentLanguage.langCode, ofType: "lproj"),
            let localizedBundle = Bundle(path: testBundlePath)
        {
            return NSLocalizedString(key, bundle: localizedBundle, comment: "")
        }
        return "?"
    }
    
  3. Dostęp do metody przez localizedString(key)

Dla tych języków z kodem skryptu, localeCode będzie langCode-scriptCode (na przykład, zh-Hans). W przeciwnym razie {[5] } będzie langCode-regionCode (na przykład pt-BR). {[11] } najpierw próbuje rozwiązać lproj przez localeCode, a następnie wraca do tylko langCode.

Jeśli nadal nie może dostać pakietu, to wraca"?"Dla ciągu, więc nie zda żadnych testów UI, które poszukaj konkretnych strun.

 6
Author: SeanR,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:02:51

Opcja 1: Ustaw domyślny język

Utwórz nowy schemat testowania interfejsu użytkownika i Ustaw domyślny język aplikacji. Spowoduje to zablokowanie aplikacji w jednym zlokalizowanym pliku, dzięki czemu można zapisać wszystkie testy dla tego języka.

Ustaw opcję Z Product -> Scheme - > Manage Schemes lub ⌘⇧,. Następnie wybierz kartę Opcje i ustaw język.

Xcode-ustawia domyślny język aplikacji

Plusy : prosta, Jednorazowa zmiana.

Cons: nie może być użyty do twórz zlokalizowane zrzuty ekranu za pomocą migawki (narzędzia, które uruchamia aplikację za pomocą testowania interfejsu użytkownika i generuje zrzuty ekranu ze sklepu App Store).

Opcja 2: Użyj -accessibilityIdentifier dla zlokalizowanych łańcuchów

Zamiast uzyskiwania dostępu do elementów za pomocą wyświetlonego tekstu lub wartości, użyj accessibilityIdentifier. Jest to odczytywane przez interfejs Testing framework, ale nigdy nie pokazywane ani odczytywane użytkownikom (nawet przy włączonej dostępności). W starej dokumentacji UIAutomation Apple wspomina o używaniu tego dla funkcji programisty, które to szwy jak dobry przypadek użycia.

Możesz kontynuować ustawianie accessibilityLabel i accessibilityValue Jak zwykle, z zlokalizowanymi wersjami.

Plusy: może być używany do bardziej ogólnych rozwiązań, takich jak robienie automatycznych zrzutów ekranu.

Cons: może wymagać więcej pracy, zmieniając każdą etykietę, której potrzebujesz "unlocalized" do testowania.

 22
Author: Joe Masilotti,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-09 17:44:27

MOŻESZ PONOWNIE WYKORZYSTAĆ PAKIETY LOKALIZACYJNE PROJEKTU!

Kiedy testujesz zachowanie skrzynek wiadomości, musisz dokładnie wiedzieć, które okno wiadomości właśnie się pojawiło. Musisz skopiować lokalizację z innego schematu podczas fazy budowania.

W interfejsie testowym target -> fazy budowania - > Kopiuj zasoby pakietu, dodaj potrzebne pliki lokalizacyjne (np. Localizable.struny).

Dodaj funkcję podobną do następującej:

func localizedString(key:String) -> String {
/*1*/ let localizationBundle = NSBundle(path: NSBundle(forClass: /*2 UITestsClass*/.self).pathForResource(deviceLanguage, ofType: "lproj")!) 
/*3*/ let result = NSLocalizedString(key, bundle:localizationBundle!, comment: "") // 
    return result
}

/*1 Gets correct bundle for the localization file, see here: http://stackoverflow.com/questions/33086266/cant-get-access-to-string-localizations-in-ui-test-xcode-7 */
/*2 Replace this with a class from your UI Tests 
/*3 Gets the localized string from the bundle */

Wtedy w kodzie możesz użyć app.przyciski [localizedString ("localized.sznurek.klucz")]

Pełny artykuł jest tutaj: https://github.com/fastlane-old/snapshot/issues/321#issuecomment-159660882

 10
Author: Volodymyr Prysiazhniuk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-30 22:08:28

Jak dotąd najprostszym i niezawodnym sposobem jest odwoływanie się do elementów za pomocą elementBoundByIndex() Tak:

    let app = XCUIApplication()
    let tabBar = app.tabBars
    tabBar.buttons.elementBoundByIndex(2).tap()
    app.navigationBars.buttons.elementBoundByIndex(0).tap()
    app.tables.cells.elementBoundByIndex(2).tap()
    app.tables.elementBoundByIndex(1).cells.elementBoundByIndex(0).tap()

Możesz zgadywać / eksperymentować z tymi wartościami i znaleźć potrzebne elementy.

 5
Author: Vladimir Shutyuk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-18 18:17:34

ODPOWIEDŹ Wołodymyra bardzo mi pomogła, ale może się nie udać, jeśli nazwa folderu pakietu lokalizacyjnego różni się od nazwy urządzenia ustawionego w Migawce. Ten fragment działa dobrze dla mnie w Swift 3.0 i z językami takimi jak włoski(gdzie obecne ustawienia regionalne to "it", ale język urządzenia to "it-IT").

    func localizedString(key:String) -> String {
      let languageBundlePath = Bundle(for: PlinthUITests.self).path(forResource: deviceLanguage, ofType: "lproj") ?? Bundle(for: PlinthUITests.self).path(forResource: NSLocale.current.languageCode!, ofType: "lproj")
      let localizationBundle = Bundle(path: languageBundlePath!)
      let result = NSLocalizedString(key, bundle:localizationBundle!, comment: "")
    return result
}
 2
Author: matsoftware,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-29 18:09:05

Jeśli robisz to w celu uruchomienia migawki (a nie rzeczywistego testowania interfejsu użytkownika), to uważam, że najprostszym rozwiązaniem jest oszukiwanie i używanie HSTestingBackchannel

Jest to narzędzie, które napisałem, które pozwala na wysyłanie powiadomień z klasy UITesting do aplikacji. Następnie piszesz kod w aplikacji, który odpowiada bezpośrednio na powiadomienia.

 0
Author: Confused Vorlon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-05 18:04:35

Oprócz odpowiedzi Joe możesz również wymusić język dla testów interfejsu użytkownika bezpośrednio w kodzie testowym bez edytowania takiego schematu:

- (void)setUp
{
    [super setUp];

    self.continueAfterFailure = NO;
    XCUIApplication *app = [[XCUIApplication alloc] init];
    app.launchArguments = @[@"-AppleLanguages", @"(en)", @"-AppleLocale", @"en_EN"];
    [app launch];
}
 0
Author: Leszek Szary,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-26 14:39:45

Odpowiedź SeanR jest świetna (+1), ale jest niewielka poprawa:

Jeśli używasz lokalizacji bazowej, twój Localizable.strings może nie być zlokalizowany w Twoim języku bazowym. Nie jest to konieczne, ponieważ język bazowy byłby używany w tym przypadku. Jeśli tak, Funkcja SeanR localizedString zwróci „?“.

Poniższa rozszerzona wersja sprawdza dodatkowo język podstawowy i zwraca zlokalizowany ciąg znaków w języku bazowym:

func localizedString(_ key: String) -> String {
    let testBundle = Bundle(for: ShopEasyUITests.self)
    guard let currentLanguage = currentLanguage else { return "?" }
    if let testBundlePath = testBundle.path(forResource: currentLanguage.localeCode, ofType: "lproj"),
        let localizedBundle = Bundle(path: testBundlePath) {
        return NSLocalizedString(key, bundle: localizedBundle, comment: "")
    }
    if let testBundlePath = testBundle.path(forResource: currentLanguage.langCode, ofType: "lproj"),
        let localizedBundle = Bundle(path: testBundlePath) {
        return NSLocalizedString(key, bundle: localizedBundle, comment: "")
    }
    if let testBundlePath = testBundle.path(forResource: "Base", ofType: "lproj"),
        let localizedBundle = Bundle(path: testBundlePath) {
        return NSLocalizedString(key, bundle: localizedBundle, comment: "")
    }
    return "?"
}
 0
Author: Reinhard Männer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-31 21:31:56

Dla funkcji migawki fastlane, SnapshotHelper.swift uruchamia aplikację z tymi argumentami. Tak więc interpretując te wartości, To rozwiązanie jest deterministyczne i byłem w stanie stworzyć poprawne migawki dla wielu języków: {]}

func getLocale(str: String) -> String {
    let start = str.index(str.startIndex, offsetBy: 1)
    let end = str.index(start, offsetBy: 2)
    let range = start..<end

    var locale = str.substring(with: range)
    if locale == "en" {
        return "Base"
    }
    return locale
}

func localizedString(_ key: String) -> String {
    print("app.launchArguments \(app.launchArguments)")
    guard let localeArgIdx = app.launchArguments.index(of: "-AppleLocale") else {
        return ""
    }
    if localeArgIdx >= app.launchArguments.count {
        return ""
    }
    let str = app.launchArguments[localeArgIdx + 1]
    let locale = getLocale(str: str)
    let testBundle = Bundle(for: Snapshot.self)
    if let testBundlePath = testBundle.path(forResource: locale, ofType: "lproj") ?? testBundle.path(forResource: locale, ofType: "lproj"),
        let localizedBundle = Bundle(path: testBundlePath)
    {
        return NSLocalizedString(key, bundle: localizedBundle, comment: "")
    }
    return ""
}

Hope this helps

 0
Author: tsuz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-16 12:32:58