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