is committed to adding powerful dynamic features to Swift. For example, Swift 3 already added nearly all the infrastructure for data reflection (which is already used by the Xcode memory debugger). We should use this infrastructure to build out a powerful user-facing API. Similarly, we would like to design and build out the implementation for dynamic method reflection runtime + API support. source: https://lists.swift.org/pipermail/swift-evolution/Week- of-Mon-20160725/025676.html
Swift3_Unit_TestingTests: XCTestCase { override func setUp() { super.setUp() // Put setup code here. This method is called before the invocation of each test method in the class. } override func tearDown() { // Put teardown code here. This method is called after the invocation of each test method in the class. super.tearDown() } func testExample() { // This is an example of a functional test case. // Use XCTAssert and related functions to verify your tests produce the correct results. } func testPerformanceExample() { // This is an example of a performance test case. self.measure { // Put the code you want to measure the time of here. } } }
begin with "test" » New instance of the test case class is created for each test method » Class names do not matter » Append "Test" or "Tests" to your filename (or else focusing won't work for that file) » Ensure your file is included in only your test target
run entire suite » Control + Commad + u -> Run without compiling » Control + Command + Option + u -> Build and run test(s) under cursor (i.e. focus on single test or single file) » Command + Shift + u -> Compile the test suite (Useful for Xcode's autocomplete)
Swift3_Unit_TestingTests: XCTestCase { let subject = NSObject() // test case instantiated for each `test` method override func setUp() { super.setUp() // Put setup code here. This method is called before the invocation of each test method in the class. } override func tearDown() { // Put teardown code here. This method is called after the invocation of each test method in the class. super.tearDown() } func test_aSillyAssertion() { // a test case XCTAssertNotNil(subject) } func test_anotherAssertion() { // subject is a new instance from the above XCTAssertEqual(subject, subject) } }
access to common dependencies let commonDependency = NSObject() override func setUp() { // put universal set up here super.setUp() } override func tearDown() { // put universal tear down here super.tearDown() } }
URLSession init(session: URLSession = URLSession.shared) { self.session = session } } // then in production code, it's just let service = AuthService()
password: String, completion: (Error?) -> Void) { let urlStr = "http://example.com/auth?username=\(username)&password=\(password)" let url = URL(string: urlStr)! let task = session.dataTask(with: url) { data, response, error in completion(error) } task.resume() } You can still use a default value!
care about internal implementation. If they do, makes your code fragile. Public and private is all that really matters. In CirrusMD's codebase, @testable is never written once.
of their defining module, and methods declared as public can no longer be overridden outside of their defining module. To allow a class to be externally subclassed or a method to be externally overridden, declare them as open, which is a new access level beyond public. Imported Objective-C classes and methods are now all imported as open rather than public. Unit tests that import a module using an @testable import will still be allowed to subclass public or internal classes as well as override public or internal methods. (SE-0117)
the f@#! out. Note: Objective C classes, like NSURLSession from Cocoa will be open by default. I recommend subclass and override. It's a little less work.
initializers » Final classes and structs » Public methods defined in an extension so you can't subclass » I'm looking at you Alamofire » Abandonware » Pro-tip: Expose protocols not concrete types
Bad news: Xcode freaks out and won't autocomplete class MyTestCase: XCTestCase { let session = FakeURLSession() lazy var service: AuthService = { return AuthService(session: self.session) }()
base test case class: override func setUp() { super.setUp() UIView.setAnimationsEnabled(false) } override func tearDown() { super.tearDown() UIView.setAnimationsEnabled(true) } Not a cure all.
return !IsTestEnvironment() } // In your production code let vc = UIViewController() let modal = UIViewController() vc.present(modal, animated: AnimationsEnabled(), completion: nil) // in your test, this now works XCTAssertNotNil(vc.presentedViewController)
func setUp() { super.setUp() controller.loadView() controller.viewDidLoad() } // tests... } Pro tip: Use MVVM, VIPER, MVP or any other pattern to keep your controllers small. Use child view controllers liberally.
are asynchronous return an instance of a Future class. Then in the test suite, a fake returns an instance of a TestFuture which invokes all code synchronously.
built in async helpers. waitForExpectations(timeout:_ handler:_) Of course, this means you have somehow inject the XCTestExpectation so you can fulfill it during async execution.