
Nazywam się Dmitrij, pracuję jako tester w firmie . Niedawno ukończyłem badania nad stosunkowo nową funkcją od — a mianowicie, nad testowaniem aplikacji iOS przy użyciu natywnego frameworka testowania XCUITest.
Wcześniej przetestowałem Firebase Test Lab dla Androida i bardzo mi się to spodobało, więc postanowiłem spróbować uruchomić infrastrukturę testową projektu iOS na tych samych zasadach. Musiałem wiele googlować i nie wszystko udało się za pierwszym razem, więc postanowiłem napisać artykuł-tutorial dla tych, przed którymi to jeszcze stoi.
Więc, jeśli macie testy UI w projekcie iOS, możecie już dziś spróbować uruchomić je na rzeczywistych urządzeniach, uprzejmie udostępnionych przez Korporację Dobra. Zainteresowanych zapraszam do czytania dalej.
W narracji postanowiłem opierać się na pewnych danych wyjściowych — prywatnym repozytorium na GitHubie i systemie budowania CircleCI. Nazwa aplikacji — AmazingApp, bundleID — com.company.amazingapp. Podaję te dane od razu, aby zminimalizować ewentualne niejasności później.
Jeśli zrealizowaliście te czy inne rozwiązania w swoim projekcie w inny sposób — podzielcie się swoimi doświadczeniami w komentarzach.
1. Same testy
Tworzymy nową gałąź projektu dla testów UI:
$ git checkout develop
$ git pull
$ git checkout -b "feature/add-ui-tests"
Otwieramy projekt w XCode i tworzymy nowy cel (Target) z testami UI [XCode -> File -> New -> Target -> iOS Testing Bundle], nadajemy mu mówiącą nazwę AmazingAppUITests.

Przechodzimy do sekcji Build Phases utworzonego Targetu i sprawdzamy, czy istnieje Target Dependencies — AmazingApp, w Compile Sources — AmazingAppUITests.swift.
Dobrą praktyką jest wydzielanie różnych wariantów budowania w oddzielne Schemy (Schemes). Tworzymy schemę dla naszych testów UI [XCode -> Product -> Scheme -> New Scheme] i nadajemy jej tę samą nazwę: AmazingAppUITests.
Budowa utworzonej schemy powinna obejmować Target głównej aplikacji — AmazingApp oraz Target testów UI — AmazingAppUITests — patrz zrzut ekranu

Następnie tworzymy nową konfigurację budowy dla testów UI. W XCode klikamy na plik projektu, przechodzimy do sekcji Info. Klikamy na „+” i tworzymy nową konfigurację, na przykład XCtest. Będzie to potrzebne w przyszłości, aby uniknąć niepotrzebnych komplikacji, gdy przyjdzie do podpisywania kodu.

W waszym projekcie są co najmniej trzy Targety: główną aplikację, testy jednostkowe (bo chyba są, prawda?) i utworzony przez nas Target testów UI.
Otwieramy Target AmazingApp, zakładkę Build Settings, sekcję Code Signing Identity. Dla konfiguracji XCtest wybieramy iOS Developer. W sekcji Code Signing Style wybieramy Manual. Profil provisioning jeszcze nie został wygenerowany, ale za chwilę do niego wrócimy.
Dla Target AmazingAppUITests robimy to samo, ale w pole Product Bundle Identifier wpisujemy com.company.amazingappuitests.
2. Konfiguracja projektu w Apple Developer Program
Wchodzimy na stronę Apple Developer Program, przechodzimy do sekcji Certificates, Identifiers & Profiles, a następnie do pola App IDs w punkcie Identifiers. Tworzymy nowy App ID o nazwie AmazingAppUITests i bundleID com.company.amazingappuitests.

Teraz mamy możliwość podpisania naszych testów osobnym certyfikatem, ale… Procedura budowania wersji do testowania wymaga zbudowania samej aplikacji oraz budowy testowego runnera. W związku z tym napotykamy problem podpisania dwóch bundle ID jednym profilem provisioning. Na szczęście istnieje proste i eleganckie rozwiązanie — Wildcard App ID. Powtarzamy procedurę tworzenia nowego App ID, ale zamiast Explicit App ID wybieramy Wildcard App ID jak na zrzucie ekranu.

Na tym etapie praca z developer.apple.com jest zakończona, ale nie zamykamy okna przeglądarki. Idziemy na i czytamy o narzędziu Match od deski do deski.
Uważny czytelnik zauważył, że do korzystania z tego narzędzia potrzebujemy prywatnego repozytorium i konta, które ma dostęp zarówno do Apple Developer Program, jak i do Github. Tworzymy (jeśli nie ma takiego) konto typu InfrastructureAccount@your.company.domain, wymyślamy silne hasło, rejestrujemy je na developer.apple.com, nadajemy mu status administratora projektu. Następnie dajemy kontu dostęp do repozytorium github twojej firmy i tworzymy nowe prywatne repozytorium o nazwie na przykład AmazingAppMatch.
3. Konfiguracja Fastlane i narzędzia match
Otwieramy terminal, przechodzimy do folderu z projektem i inicjalizujemy fastlane zgodnie z tym, co jest podane w Po wprowadzeniu komendy
$ fastlane initzostanie zaproponowane wybranie dostępnych konfiguracji użycia. Wybieramy czwarty punkt — ręczna konfiguracja projektu.

W projekcie pojawił się nowy katalog fastlane, w którym znajdują się dwa pliki — Appfile i Fastfile. Krótko mówiąc — w Appfile przechowujemy dane administracyjne, a w Fastfile zapisujemy zadania, w terminologii Fastlane nazywane lanes. Polecam do przeczytania oficjalną dokumentację: , .
Otwieramy Appfile w ulubionym edytorze tekstu i przekształcamy go do następującego wyglądu:
app_identifier "com.company.amazingapp" # Bundle ID
apple_dev_portal_id "infrastructureaccount@your.company.domain" # Utworzony konto infrastruktury, które ma uprawnienia do edytowania projektu iOS w Apple Developer Program.
team_id "LSDY3IFJAY9" # Identyfikator zespołu w Portalu Dewelopera
Wracamy do terminala i zgodnie z oficjalnym podręcznikiem zaczynamy konfigurować match.
$ fastlane match init
$ fastlane match development
Następnie wprowadzamy wymagane dane — repozytorium, konto, hasło itd.
Ważne: przy pierwszym uruchomieniu narzędzie match poprosi o podanie hasła do odszyfrowania repozytorium. Bardzo ważne jest, aby zapisać to hasło, przyda się na etapie konfiguracji serwera CI!
W folderze fastlane pojawił się nowy plik — Matchfile. Otwieramy go w ulubionym edytorze tekstu i przekształcamy go tak:
git_url("https://github.com/YourCompany/AmazingAppMatch") # Utworzone prywatne repozytorium do przechowywania certyfikatów i profili.
type("development") # Domyślny typ, może być: appstore, adhoc, enterprise lub development
app_identifier("com.company.amazingapp")
username("infrastructureaccount@your.company.domain") # Twój login do Portalu Dewelopera Apple dla konta infrastruktury
Wypełniamy dokładnie w ten sposób, jeśli chcemy później użyć match do podpisywania buildów do publikacji w Crashlytics i/lub App Store, tzn. do podpisywania bundle ID Twojej aplikacji.
Ale, jak pamiętamy, do podpisywania testowego builda stworzyliśmy specjalny Wildcard ID. Dlatego otwieramy Fastfile i wpisujemy nowy lane:
lane :testing_build_for_firebase do
match(
type: "development",
readonly: true,
app_identifier: "com.company.*",
git_branch: "uitests" # tworzymy osobną gałąź dla certyfikatu development do podpisywania testowej wersji.
)
end
Zapisujemy, wprowadzamy w terminalu
fastlane testing_build_for_firebasei widzimy, jak fastlane stworzył nowy certyfikat i umieścił go w repozytorium. Świetnie!
Otwieramy XCode. Teraz mamy odpowiedni profil provisioningowy w postaci Match Development com.company.*, który należy wskazać w sekcji Provisioning profile dla celów AmazingApp i AmazingAppUITests.

Pozostaje dopisać lane do budowy testów. Idziemy do projektu wtyczki do fastlane, która ułatwia konfigurację eksportu do Firebase Test Lab i postępujemy zgodnie z instrukcjami.
Kopiujemy z oryginalnego przykładu, aby nasz lane testing_build_for_firebase ostatecznie wyglądał tak:
lane :testing_build_for_firebase do
match(
type: "development",
readonly: true,
app_identifier: "com.company.*",
git_branch: "uitests"
)
scan(
scheme: 'AmazingAppUITests', # Schemat testów UI
clean: true, # Zalecane: zapewni to, że budowa nie będzie zawierała zbędnych plików
skip_detect_devices: true, # Wymagane
build_for_testing: true, # Wymagane
sdk: 'iphoneos', # Wymagane
should_zip_build_products: true, # Musi być prawda, aby ustawić poprawny format dla Firebase Test Lab
)
firebase_test_lab_ios_xctest(
gcp_project: 'AmazingAppUITests', # Nazwa twojego projektu Google Cloud (do tej linii wrócimy później)
devices: [ # Urządzenie(a), na których będą przeprowadzane testy
{
ios_model_id: 'iphonex', # ID modelu urządzenia, zobacz polecenie gcloud powyżej
ios_version_id: '12.0', # ID wersji iOS, zobacz polecenie gcloud powyżej
locale: 'en_US', # Opcjonalne: domyślnie ustawione na en_US, jeśli nie ustawione
orientation: 'portrait' # Opcjonalne: domyślnie ustawione na portret, jeśli nie ustawione
}
]
)
end
Aby uzyskać pełne informacje o konfiguracji fastlane w CircleCI, polecam przeczytać oficjalną dokumentację. .
Nie zapominajmy o dodaniu nowego zadania do naszego config.yml:
build-for-firebase-test-lab:
macos:
xcode: "10.1.0"
working_directory: ~\/project
shell: \/bin\/bash --login -o pipefail
steps:
- checkout
- attach_workspace:
at: ~\/project
- run: sudo bundle install # aktualizujemy zależności
- run:
name: install gcloud-sdk # na maszynie mac należy zainstalować gcloud
command: |
ruby -e "$(curl -fsSL https:\/\/raw.githubusercontent.com\/Homebrew\/install\/master\/install)" < \/dev\/{nul} 2> \/dev\/null ; brew install caskroom\/cask\/brew-cask 2> \/dev\/null
brew cask install google-cloud-sdk
- run:
name: build app for testing
command: fastlane testing_build_for_firebase # uruchamiamy lane budowy i wysyłamy do firebase
4. A co z naszym testowym środowiskiem? Konfigurujemy Firebase.
Przechodzimy teraz do rzeczy, dla której napisano ten artykuł.
Możliwe, że twoja aplikacja korzysta z Firebase na darmowym planie, być może — wcale nie korzysta. Nie ma to zasadniczego znaczenia, ponieważ na potrzeby testowania możemy stworzyć osobny projekt z rokiem darmowego korzystania (super, prawda?)
Logujemy się na nasze konto infrastrukturalne (lub jakiekolwiek inne, to nie ma znaczenia), i przechodzimy na . Tworzymy nowy projekt o nazwie AmazingAppUITests.
Ważne: W poprzednim kroku w Fastfile w lane firebase_test_lab_ios_xctest parametr gcp_project powinien odpowiadać nazwie projektu.

Domyślne ustawienia są dla nas w zupełności wystarczające.
Nie zamykamy zakładki, pod tym samym kontem rejestrujemy się w — to konieczna miara, ponieważ komunikacja z Firebase odbywa się za pośrednictwem interfejsu konsoli gcloud.
Google oferuje 300$ na rok, co w kontekście przeprowadzania testów automatycznych jest równoważne z rocznym bezpłatnym korzystaniem z usługi. Wprowadzamy dane płatnicze, czekamy na testowe obciążenie 1$ i otrzymujemy 300$ na koncie. Po upływie roku projekt zostanie automatycznie przeniesiony na bezpłatny plan taryfowy, więc nie ma się co martwić o możlost utraty pieniędzy.
Wracamy do zakładki z projektem Firebase i przenosimy go na plan taryfowy Blaze — teraz mamy czym zapłacić w przypadku przekroczenia limitu.
W interfejsie gcloud wybieramy nasz projekt Firebase, klikamy w głównym menu „Katalog”, a następnie dodajemy Cloud Testing API oraz Cloud Tools Result API.

Następnie przechodzimy do punktu menu „IAM i administracja” -> Konta serwisowe -> Utwórz konto serwisowe. Przyznajemy prawa do edytowania projektu.

Tworzymy klucz API w formacie JSON

Pobranego JSON-a będziemy potrzebować trochę później, a na razie uważamy konfigurację Test Lab za zakończoną.
5. Konfiguracja CircleCI
Powstaje słuszne pytanie — co zrobić z hasłami? Bezpieczne przechowywanie naszych haseł i innych wrażliwych danych pomoże nam mechanizm zmiennych środowiskowych naszej maszyny budowlanej. W ustawieniach projektu CircleCI wybieramy Zmienne środowiskowe

I tworzymy następujące zmienne:
- key: GOOGLE_APPLICATION_CREDENTIALS
value: zawartość pliku json z kluczem konta serwisowego gcloud - key: MATCH_PASSWORD
value: hasło do odszyfrowania repozytorium github z certyfikatami - key: FASTLANE_PASSWORD
value: hasło konta infrastrukturalnego w Apple Developer Portal
Zapisujemy zmiany, tworzymy PR i wysyłamy na przegląd do naszego lidera zespołu.
Podsumowanie
W wyniku wykonania tych prostych czynności uzyskaliśmy solidne, stabilnie działające środowisko z możliwością nagrywania wideo na ekranie urządzenia w czasie testowania. W przykładowym teście podałem model urządzenia iPhone X, ale farmy oferują bogaty wybór kombinacji różnych modeli i wersji iOS.
Druga część będzie poświęcona krok po kroku konfiguracji Firebase Test Lab dla projektu na Androida.
Źródło: habr.com
