Jedną irytującą rzeczą podczas uruchamiania testów w Xcode 6.1 jest to, że cała aplikacja musi działać i uruchamiać swój scenorys i kontroler widoku głównego. W mojej aplikacji uruchamia to niektóre wywołania serwera, które pobierają dane API. Jednak nie chcę, aby aplikacja robiła to podczas uruchamiania testów.
Gdy zniknęły makra preprocesora, co jest najlepsze dla mojego projektu, aby był świadomy, że został uruchomiony na podstawie testów, a nie zwykłego uruchomienia? Uruchamiam je normalnie z command+ Ui na bocie.
Pseudo kod:
// Appdelegate.swift
if runningTests() {
return
} else {
// do ordinary api calls
}
Odpowiedzi:
Zamiast sprawdzać, czy testy są uruchomione, aby uniknąć skutków ubocznych, możesz uruchomić je bez samej aplikacji hosta. Przejdź do Ustawienia projektu -> wybierz cel testu -> Ogólne -> Testowanie -> Aplikacja hosta -> wybierz „Brak”. Pamiętaj tylko, aby uwzględnić wszystkie pliki potrzebne do uruchomienia testów, a także biblioteki zwykle zawarte w celu aplikacji Host.
źródło
Odpowiedź Elvinda nie jest zła, jeśli chcesz mieć coś, co zwykło się nazywać „testami logiki”. Jeśli nadal chcesz uruchomić swoją aplikację hosta, ale warunkowo wykonać lub nie wykonać kodu, w zależności od tego, czy testy są uruchomione, możesz skorzystać z następujących czynności, aby wykryć, czy pakiet testowy został wstrzyknięty:
if NSProcessInfo.processInfo().environment["XCTestConfigurationFilePath"] != nil { // Code only executes when tests are running }
Użyłem flagi kompilacji warunkowej, jak opisano w tej odpowiedzi, aby koszt czasu wykonania był ponoszony tylko w kompilacjach debugowania:
#if DEBUG if NSProcessInfo.processInfo().environment["XCTestConfigurationFilePath"] != nil { // Code only executes when tests are running } #endif
Edytuj Swift 3.0
if ProcessInfo.processInfo.environment["XCTestConfigurationFilePath"] != nil { // Code only executes when tests are running }
źródło
XCTestConfigurationFilePath
klucza środowiska zamiastXCInjectBundle
.po ProcessInfo.processInfo.environment
nie ma kluczaXCTestConfigurationFilePath
. Czy możesz udostępnić swój kod? Jest to sprawdzane pod kątem celu UITestNSClassFromString("XCTest")
poniższą metodę?Używam tego w aplikacji: didFinishLaunchingWithOptions:
// Return if this is a unit test if let _ = NSClassFromString("XCTest") { return true }
źródło
Inny, moim zdaniem prostszy sposób:
Edytuj swój schemat, aby przekazać wartość logiczną jako argument uruchamiania do swojej aplikacji. Lubię to:
Wszystkie argumenty uruchamiania są automatycznie dodawane do pliku
NSUserDefaults
.Możesz teraz uzyskać BOOL jak:
BOOL test = [[NSUserDefaults standardUserDefaults] boolForKey:@"isTest"];
źródło
"XCTestConfigurationFilePath"
lubNSClassFromString("XCTest")
. Wdrożyłem to rozwiązanie w Swift z funkcją globalnąfunc isRunningTests() -> Bool { return UserDefaults.standard.bool(forKey: "isRunningTests") }
Uważam, że chęć sprawdzenia, czy bierzesz udział w teście, czy nie, jest całkowicie uzasadniona. Istnieje wiele powodów, dla których może to być pomocne. Na przykład podczas uruchamiania testów wcześnie wracam z metod uruchamiania aplikacji / will-finish-launching w delegacie aplikacji, dzięki czemu testy zaczynają się szybciej dla kodu niezwiązanego z moim testem jednostkowym. Jednak nie mogę przejść testu „logiki” z wielu innych powodów.
Używałem doskonałej techniki opisanej powyżej przez @Michael McGuire. Jednak zauważyłem, że przestał działać dla mnie w okolicach Xcode 6.4 / iOS8.4.1 (być może wcześniej się zepsuł).
Mianowicie, nie widzę już XCInjectBundle podczas uruchamiania testu wewnątrz celu testowego dla mojej struktury. Oznacza to, że działam wewnątrz celu testowego, który testuje strukturę.
Tak więc, stosując podejście sugerowane przez @Fogmeister, każdy z moich schematów testowych ustawia teraz zmienną środowiskową, którą mogę sprawdzić.
Następnie mam kod na zajęciach o nazwie,
APPSTargetConfiguration
który może odpowiedzieć na to proste pytanie.static NSNumber *__isRunningTests; + (BOOL)isRunningTests; { if (!__isRunningTests) { NSDictionary *environment = [[NSProcessInfo processInfo] environment]; NSString *isRunningTestsValue = environment[@"APPS_IS_RUNNING_TEST"]; __isRunningTests = @([isRunningTestsValue isEqualToString:@"YES"]); } return [__isRunningTests boolValue]; }
Jedynym zastrzeżeniem związanym z tym podejściem jest to, że jeśli uruchomisz test z głównego schematu aplikacji, na co pozwoli XCTest (to znaczy nie wybierzesz jednego ze schematów testowych), nie otrzymasz tego zestawu zmiennych środowiskowych.
źródło
var isRunningTests: Bool { return ProcessInfo.processInfo.environment["XCTestConfigurationFilePath"] != nil }
Stosowanie
if isRunningTests { return "lena.bmp" } return "facebook_profile_photo.bmp"
źródło
Połączone podejście @Jessy i @Michael McGuire
(Jako przyjęta odpowiedź nie pomoże ci przy tworzeniu frameworka)
Oto kod:
#if DEBUG if (NSClassFromString(@"XCTest") == nil) { // Your code that shouldn't run under tests } #else // unconditional Release version #endif
źródło
Oto sposób, w jaki używałem w Swift 4 / Xcode 9 do naszych testów jednostkowych. Opiera się na odpowiedzi Jessego .
Nie jest łatwo w ogóle uniemożliwić załadowanie storyboardu, ale jeśli dodasz to na początku didFinishedLaunching, wtedy programistom będzie jasne, co się dzieje:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { #if DEBUG if let _ = NSClassFromString("XCTest") { // If we're running tests, don't launch the main storyboard as // it's confusing if that is running fetching content whilst the // tests are also doing so. let viewController = UIViewController() let label = UILabel() label.text = "Running tests..." label.frame = viewController.view.frame label.textAlignment = .center label.textColor = .white viewController.view.addSubview(label) self.window!.rootViewController = viewController return true } #endif
(oczywiście nie powinieneś robić niczego takiego w przypadku testów interfejsu użytkownika, w których chcesz, aby aplikacja uruchamiała się normalnie!)
źródło
Możesz przekazać argumenty środowiska uruchomieniowego do aplikacji w zależności od schematu tutaj ...
Ale kwestionowałbym, czy jest to rzeczywiście potrzebne.
źródło
To jest szybki sposób na zrobienie tego.
extension Thread { var isRunningXCTest: Bool { for key in self.threadDictionary.allKeys { guard let keyAsString = key as? String else { continue } if keyAsString.split(separator: ".").contains("xctest") { return true } } return false } }
A tak z tego korzystasz:
if Thread.current.isRunningXCTest { // test code goes here } else { // other code goes here }
Oto cały artykuł:
https://medium.com/@theinkedengineer/check-if-app-is-running-unit-tests-the-swift-way-b51fbfd07989
źródło
return threadDictionary.allKeys.anyMatch { ($0 as? String)?.split(separator: ".").contains("xctest") == true }
Niektóre z tych podejść nie działają z UITests i jeśli zasadniczo testujesz za pomocą samego kodu aplikacji (zamiast dodawać określony kod do celu UITest).
Skończyło się na ustawieniu zmiennej środowiskowej w metodzie setUp testu:
XCUIApplication *testApp = [[XCUIApplication alloc] init]; // set launch environment variables NSDictionary *customEnv = [[NSMutableDictionary alloc] init]; [customEnv setValue:@"YES" forKey:@"APPS_IS_RUNNING_TEST"]; testApp.launchEnvironment = customEnv; [testApp launch];
Zauważ, że jest to bezpieczne dla moich testów, ponieważ obecnie nie używam żadnych innych wartości launchEnvironment; jeśli to zrobisz, możesz oczywiście najpierw skopiować wszelkie istniejące wartości.
Następnie w kodzie aplikacji szukam tej zmiennej środowiskowej, jeśli / kiedy chcę wykluczyć niektóre funkcje podczas testu:
BOOL testing = false; ... if (! testing) { NSDictionary *environment = [[NSProcessInfo processInfo] environment]; NSString *isRunningTestsValue = environment[@"APPS_IS_RUNNING_TEST"]; testing = [isRunningTestsValue isEqualToString:@"YES"]; }
Uwaga - dzięki za komentarz RishiG, który dał mi ten pomysł; Właśnie rozszerzyłem to na przykład.
źródło
Metoda, której używałem, przestała działać w Xcode 12 beta 1. Po wypróbowaniu wszystkich odpowiedzi opartych na kompilacji na to pytanie, zainspirowała mnie odpowiedź @ ODB. Oto wersja Swift dość prostego rozwiązania, które działa zarówno na rzeczywistych urządzeniach, jak i symulatorach. Powinien również być dość „dowód zwolnienia”.
Wstaw w konfiguracji testowej:
let app = XCUIApplication() app.launchEnvironment.updateValue("YES", forKey: "UITesting") app.launch()
Wstaw do aplikacji:
let isTesting: Bool = (ProcessInfo.processInfo.environment["UITesting"] == "YES")
Aby z niego skorzystać:
if isTesting { // Only if testing } else { // Only if not testing }
źródło
Pracował dla mnie:
Cel C
[[NSProcessInfo processInfo].environment[@"DYLD_INSERT_LIBRARIES"] containsString:@"libXCTTargetBootstrapInject"]
Szybki:
ProcessInfo.processInfo.environment["DYLD_INSERT_LIBRARIES"]?.contains("libXCTTargetBootstrapInject") ?? false
źródło
Najpierw dodaj zmienną do testowania:
i użyj tego w swoim kodzie:
if ProcessInfo.processInfo.environment["IS_UNIT_TESTING"] == "1" { // Code only executes when tests are running }
źródło
Najwyraźniej w Xcode12 musimy szukać w kluczu środowiska
XCTestBundlePath
zamiast tego,XCTestConfigurationFilePath
czy używasz nowegoXCTestPlan
źródło