Unit-Tests in Swift
Being quick and nimble
After our excursion to Unit-Tests with Objective-C let's return to our project. Swift is similar and at the same time entirely different. Suddenly we are statically typed, have access modifiers and can't work with the runtime. Introducing Quick and Nimble. They are RSpec in Swift.
Quick
As with Kiwi I prefer syntactic sugar, which is provided by Quick. Once again we have the same state types as usual in Behavior Driven Development (BDD) based framework:
- Describe
- Context
Nimble
Instead of having just one framework, the Quick project provides Nimble for assertions. As before this is mainly about readability and syntactic sugar rather than providing features not being in XCTest.
expect(sut).notTo(beNil())
Hacking Private
While we had categories in Objective-C we can't use the same trick for Swift. Instead there is the @testable keyword. Importing a module with this, will provide all the module internal variable and functions. Regrettably private and fileprivate methods are not included. But wanting to test these, shows you are coupling your tests too tightly to your software.
@testable import SomeClass
Mock Objects
There is no proficient mock framework for Swift. This results in us having to create our own techniques to mock objects. My preferred way coincides with the way I develop. In my opinion you should not pass specific classes. Instead use protocols. Having for every passable class a protocol will help us replacing them with mocks.
In our system under test, we use protocols as types for our dependencies:
var testObject: TestProtocol = TestClass()
With the protocol in place, we can create our mock class:
class TestMock: TestProtocol {
//implemented method stubs
}
Having this accessible in tests, we can initiate the object and replace the dependency with our mock:
it("") {
let mock = NetworkMock()
sut.network = mock
....
}
Sooo, what will we do using Classes we don't own? Every day we have them: NSUserDefaults, AVAudioPlayer, UNUserNotificationCenter and more.
It's rather simple. Using the same technique, there is also the option to replace class we do not own. Just create a protocol with the methods you will be using and with an extension you can make this class conform to your protocol e.g. UserDefaults:
Conclusion
Knowing these basic techniques we will be able to work test-driven in our app. It's not as easy as it was with Objective-C but maybe having these obstacles will improve our ability to write the right tests.
Next: Testing Apples MVC
Previous: Unit-Tests in Objective-C