Test
Test
Test Pattern
Unit Test
Every unit test follows this general pattern:
- setup some object under test
- provide the object under test some input
- make assertions on the objects outputs
Skip Tests
You can append skip to the test_function_name in order for Xcode compiler | runtime service to skip those tests in order to be ran and not wasted with that resources accordingly.
Really handy when you're working with flaky tests
Also adding test repetitions for flaky tests in swift.
avanderlee | Flaky tests resolving using Test Repetitions in Xcode
Conditional Skip Testing
You could utilize the various ways to skip testing if certain conditions have been met.
guard let envValue = ProcessInfo.processInfo.environment["IS_CI_SERVER"] else { return }
try XCTSkipIf(envValue == "true")
You could embed this code in Test life cycle methods which is present for setting up code / environment before running the main test case.
- func setUp() { }
- func setUpWithError() throws { }
The setUpWithError is the right candidate for skipping tests without adding that skip_logic for every unit tests. You can just include that information in the Test_Class file.
Clean and less verbose.
SO | skip-multi-tests-in-xcode
setUp
class PPVSSLoaderDelegateTests: XCTestCase {
var mockVSSDelegate: MockVSSDelegate!
override func setUp()Â {
   mockVSSDelegate = MockVSSDelegate()
  }
}
Teardown
class PPVSSLoaderDelegateTests: XCTestCase {
var mockVSSDelegate: MockVSSDelegate!
override func tearDown()Â {
   mockVSSDelegate.reset()
  }
}
Warnings
Errors thrown from here are not handled - this appears when you're trying to call a function which can throw an error. Swift needs to either do a try catch to handle the thrown error or mark the test function with throws
func test_whenSomething_ThenBehavior() throws {
let unwrappedDate = try callUnwrappingFunction() // Warning
}
Unwrapping while Testing
We should be careful to use force unwrapping when handling optionals in tests. if for some reason it's nil the tests are going to crash. that's not a super big deal when running locally but if that happens when running in CI it's going to force a restart of the tests and not give us much to go on as to why the crash occurred. better to treat this case as a test failure rather than crashing so that the tests report the failure and then continue - Test guru - mentor
func test_objectCreation_UsingJSONMapping() {
let object: ObjectType = getMappedObject(for: TestJSON.Object)!
}
Solution
func test_objectCreation_UsingJSONMapping() throws {
let object: ObjectType = try XCTUnwrap(getMappedObject(for: TestJSON.Object))
}
Testable
In order to access internal classes to our unit / ui test target, we can utilize the following keyword testable in order to gain elevated access to its internal implementation.
@testable import MainAppModuleName
Note:
- We still can't access private functions or variables with this.
- You can resort to
private(set)for the properties to make tests which can be just read for unit testing.
how-to-unit-test-private-methods-in-swift
Mirror | Reflection
Reflection is a common programming language feature that enables us to inspect, and work with, the members of a type — dynamically, at runtime.
avanderlee | reflection how mirror works
// Foo class.
@interface Foo : NSObject
- (void)hello;
@end
// Sending "hello" to a Foo instance without reflection.
Foo *obj = [[Foo alloc] init];
[obj hello];
// Sending "hello" to a Foo instance with reflection.
id obj = [[NSClassFromString(@"Foo") alloc] init];
[obj performSelector: @selector(hello)];
Assertions
– assert()
– precondition()
– assertionFailure()
– preconditionFailure()
– fatalError()
assert(10 > 5, "10 is not less than 5")
Swift by sundell | picking-the-right-way-of-failing-in-swift
Simulators vs emulators
testing-on-real-devices-vs-simulators-vs-emulators
CI / CD
speed-up-ios-ci-using-test-without-building-xctestrun-and-fastlane
Errors
Links
apple dev | xcTest setup with error
xctest-setup-and-teardown-are-not-dead-yet