Eine ärgerliche Sache beim Ausführen von Tests in Xcode 6.1 ist, dass die gesamte App ihr Storyboard und ihren Root View Controller ausführen und starten muss. In meiner App werden einige Serveraufrufe ausgeführt, die API-Daten abrufen. Ich möchte jedoch nicht, dass die App dies tut, wenn sie ihre Tests ausführt.
Was ist das Beste für mein Projekt, wenn Präprozessor-Makros weg sind, um zu wissen, dass es mit Tests gestartet wurde und kein gewöhnlicher Start? Ich führe sie normal mit command+ Uund auf einem Bot aus.
Pseudocode:
// Appdelegate.swift
if runningTests() {
return
} else {
// do ordinary api calls
}
Antworten:
Anstatt zu überprüfen, ob die Tests ausgeführt werden, um Nebenwirkungen zu vermeiden, können Sie die Tests auch ohne die Host-App selbst ausführen. Gehen Sie zu Projekteinstellungen -> wählen Sie das Testziel aus -> Allgemein -> Testen -> Hostanwendung -> wählen Sie 'Keine'. Denken Sie daran, alle Dateien, die Sie zum Ausführen der Tests benötigen, sowie Bibliotheken einzuschließen, die normalerweise vom Host-App-Ziel enthalten sind.
quelle
Elvinds Antwort ist nicht schlecht, wenn Sie so etwas wie reine "Logiktests" haben möchten. Wenn Sie Ihre enthaltende Hostanwendung weiterhin ausführen möchten, aber Code abhängig davon ausführen oder nicht ausführen möchten, je nachdem, ob Tests ausgeführt werden, können Sie Folgendes verwenden, um festzustellen, ob ein Testpaket injiziert wurde:
if NSProcessInfo.processInfo().environment["XCTestConfigurationFilePath"] != nil { // Code only executes when tests are running }
Ich habe ein bedingtes Kompilierungsflag verwendet, wie in dieser Antwort beschrieben, damit die Laufzeitkosten nur bei Debug-Builds anfallen:
#if DEBUG if NSProcessInfo.processInfo().environment["XCTestConfigurationFilePath"] != nil { // Code only executes when tests are running } #endif
Bearbeiten Sie Swift 3.0
if ProcessInfo.processInfo.environment["XCTestConfigurationFilePath"] != nil { // Code only executes when tests are running }
quelle
XCTestConfigurationFilePath
Umgebungsschlüssel anstelle vonXCInjectBundle
.po ProcessInfo.processInfo.environment
hat keinen SchlüsselXCTestConfigurationFilePath
. Können Sie bitte Ihren Code teilen? Dies wird gegen ein UITest-Ziel überprüftNSClassFromString("XCTest")
Methode ausprobiert ?Ich benutze dies in der Anwendung: didFinishLaunchingWithOptions:
// Return if this is a unit test if let _ = NSClassFromString("XCTest") { return true }
quelle
Andere, meiner Meinung nach einfacher:
Sie bearbeiten Ihr Schema, um einen booleschen Wert als Startargument an Ihre App zu übergeben. So was:
Alle Startargumente werden automatisch zu Ihrem hinzugefügt
NSUserDefaults
.Sie können das BOOL jetzt wie folgt erhalten:
BOOL test = [[NSUserDefaults standardUserDefaults] boolForKey:@"isTest"];
quelle
"XCTestConfigurationFilePath"
oder verlassenNSClassFromString("XCTest")
. Ich habe diese Lösung in Swift mit einer globalen Funktion implementiertfunc isRunningTests() -> Bool { return UserDefaults.standard.bool(forKey: "isRunningTests") }
Ich glaube, es ist völlig legitim zu wissen, ob Sie in einem Test laufen oder nicht. Es gibt zahlreiche Gründe, warum dies hilfreich sein kann. Wenn ich beispielsweise Tests ausführe, kehre ich frühzeitig von den Methoden zum Starten der Anwendung / zum Beenden der Anwendung im App-Delegaten zurück, sodass die Tests für Code, der für meinen Komponententest nicht relevant ist, schneller beginnen. Aus vielen anderen Gründen kann ich jedoch keinen reinen "Logik" -Test machen.
Ich habe die ausgezeichnete Technik verwendet, die oben von @Michael McGuire beschrieben wurde. Mir ist jedoch aufgefallen, dass Xcode 6.4 / iOS8.4.1 für mich nicht mehr funktioniert (möglicherweise ist es früher kaputt gegangen).
Ich sehe nämlich das XCInjectBundle nicht mehr, wenn ich einen Test in einem Testziel für ein Framework von mir ausführe. Das heißt, ich laufe in einem Testziel, das ein Framework testet.
Unter Verwendung des von @Fogmeister vorgeschlagenen Ansatzes legt jedes meiner Testschemata jetzt eine Umgebungsvariable fest, nach der ich suchen kann.
Dann ist hier ein Code, den ich für eine Klasse namens habe
APPSTargetConfiguration
, der diese einfache Frage für mich beantworten kann.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]; }
Die einzige Einschränkung bei diesem Ansatz besteht darin, dass Sie diese Umgebungsvariable nicht erhalten, wenn Sie einen Test über Ihr Haupt-App-Schema ausführen, wie Sie es mit XCTest tun können (dh keines Ihrer Testschemata auswählen).
quelle
var isRunningTests: Bool { return ProcessInfo.processInfo.environment["XCTestConfigurationFilePath"] != nil }
Verwendung
if isRunningTests { return "lena.bmp" } return "facebook_profile_photo.bmp"
quelle
Kombinierter Ansatz von @Jessy und @Michael McGuire
(Als akzeptierte Antwort hilft Ihnen nicht bei der Entwicklung eines Frameworks)
Also hier ist der Code:
#if DEBUG if (NSClassFromString(@"XCTest") == nil) { // Your code that shouldn't run under tests } #else // unconditional Release version #endif
quelle
Hier ist eine Möglichkeit, die ich in Swift 4 / Xcode 9 für unsere Unit-Tests verwendet habe. Es basiert auf Jesses Antwort .
Es ist nicht einfach zu verhindern, dass das Storyboard überhaupt geladen wird. Wenn Sie dies jedoch zu Beginn von didFinishedLaunching hinzufügen, wird Ihren Entwicklern klar, was los ist:
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
(Sie sollten so etwas natürlich nicht für UI-Tests tun, bei denen die App wie gewohnt gestartet werden soll!)
quelle
Sie können Laufzeitargumente je nach Schema hier an die App übergeben ...
Aber ich würde fragen, ob es tatsächlich benötigt wird oder nicht.
quelle
Dies ist der schnelle Weg, dies zu tun.
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 } }
Und so benutzt du es:
if Thread.current.isRunningXCTest { // test code goes here } else { // other code goes here }
Hier ist der vollständige Artikel:
https://medium.com/@theinkedengineer/check-if-app-is-running-unit-tests-the-swift-way-b51fbfd07989
quelle
return threadDictionary.allKeys.anyMatch { ($0 as? String)?.split(separator: ".").contains("xctest") == true }
Einige dieser Ansätze funktionieren nicht mit UITests und wenn Sie im Grunde genommen mit dem App-Code selbst testen (anstatt einem UITest-Ziel bestimmten Code hinzuzufügen).
Am Ende habe ich eine Umgebungsvariable in der setUp-Methode des Tests festgelegt:
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];
Beachten Sie, dass dies für meine Tests sicher ist, da ich derzeit keine anderen launchEnvironment-Werte verwende. In diesem Fall möchten Sie natürlich zuerst alle vorhandenen Werte kopieren.
Dann suche ich in meinem App-Code nach dieser Umgebungsvariablen, wenn ich während eines Tests einige Funktionen ausschließen möchte:
BOOL testing = false; ... if (! testing) { NSDictionary *environment = [[NSProcessInfo processInfo] environment]; NSString *isRunningTestsValue = environment[@"APPS_IS_RUNNING_TEST"]; testing = [isRunningTestsValue isEqualToString:@"YES"]; }
Hinweis - danke für den Kommentar von RishiG, der mir diese Idee gegeben hat; Ich habe das nur zu einem Beispiel erweitert.
quelle
Die Methode, die ich verwendet hatte, funktionierte in Xcode 12 Beta 1 nicht mehr. Nachdem ich alle Build-basierten Antworten auf diese Frage ausprobiert hatte, ließ ich mich von der Antwort von @ ODB inspirieren. Hier ist eine Swift-Version einer ziemlich einfachen Lösung, die sowohl für reale Geräte als auch für Simulatoren funktioniert. Es sollte auch ziemlich "Release Proof" sein.
In Testaufbau einfügen:
let app = XCUIApplication() app.launchEnvironment.updateValue("YES", forKey: "UITesting") app.launch()
In App einfügen:
let isTesting: Bool = (ProcessInfo.processInfo.environment["UITesting"] == "YES")
Um es zu benutzen:
if isTesting { // Only if testing } else { // Only if not testing }
quelle
Arbeitete für mich:
Ziel c
[[NSProcessInfo processInfo].environment[@"DYLD_INSERT_LIBRARIES"] containsString:@"libXCTTargetBootstrapInject"]
Schnell:
ProcessInfo.processInfo.environment["DYLD_INSERT_LIBRARIES"]?.contains("libXCTTargetBootstrapInject") ?? false
quelle
Fügen Sie zuerst eine Variable zum Testen hinzu:
und verwenden Sie das in Ihrem Code:
if ProcessInfo.processInfo.environment["IS_UNIT_TESTING"] == "1" { // Code only executes when tests are running }
quelle
Anscheinend müssen wir in Xcode12 im Umgebungsschlüssel suchen,
XCTestBundlePath
anstattXCTestConfigurationFilePath
wenn Sie den neuen verwendenXCTestPlan
quelle