Unit Testing – swifting.io http://swifting.io Sharing passion in Swift Tue, 25 Jun 2019 07:51:48 +0000 en hourly 1 https://wordpress.org/?v=5.2.5 http://swifting.io/wp-content/uploads/2016/10/swift_4_ptak_40px-1.png Unit Testing – swifting.io http://swifting.io 32 32 #50 Synchronous Unit Tests http://swifting.io/blog/2018/03/03/50-synchronous-unit-tests/?utm_source=rss&utm_medium=rss&utm_campaign=50-synchronous-unit-tests http://swifting.io/blog/2018/03/03/50-synchronous-unit-tests/#comments Sat, 03 Mar 2018 07:37:21 +0000 http://swifting.io/?p=1017 Asynchronous Expectations

Mobile applications are usually multi-threaded. We perform UI operations on the main thread and dispatch heavy tasks (e.g. network requests, JSON parsing, writing to a file on a disk) on background threads. The iOS allows us to use backgrounds threads for example by using Grand Central Dispatch API (GCD), i.e. by performing operations on DispatchQueue objects. Work dispatched to a background DispatchQueue is usually done asynchronously with queue.async{} call.

If we wanted to test an object that uses a queue to perform work in background, we would use an XCTestExpectation and wait for an async operation to finish. We can fulfil the expectation in a callback.

func testCompletionGetsAtLeast1Message() {

    //Arrange
    let laoder = MessageLoader()
    let expectation = XCTestExpectation(description: "should call completion handler with at least 1 message")

    //Act
    laoder.load { messages in
        //Assert
        XCTAssertFalse(messages.isEmpty)
        expectation.fulfill()
    }
    
    wait(for: [expectation], timeout: 5)
}

In autumn I asked on Twitter about the best way to wait for an XCTestExpectation. The interface of XCTestCase class declares a few methods:

I didn’t get a clear answer, but the reply gave food for thought…

Asynchronous drawbacks

Unclear AAA pattern

It’s a good practice to divide test into 3 phases:
Arrange – setup a subject under test, set it in the desired state
Act – perform an action on the subject
Assert – test if a desired behaviour or change happened

In an asynchronous test the phases aren’t clearly visible. How would you name the last phase? A Wait phase?

func testCompletionGetsAtLeast1Message() {

    //Arrange
    let laoder = MessageLoader()
    let expectation = XCTestExpectation(description: "should call completion handler with at least 1 message")

    //Act
    laoder.load { messages in
        //Assert
        XCTAssertFalse(messages.isEmpty)
        expectation.fulfill()
    }
    
    //???
    wait(for: [expectation], timeout: 5)
}

Overhead

In the code snippet above we wait 5 seconds for the expectation. It means that our unit test can take up to 5 seconds in the worst case scenario (i.e. if execution of load(:) method took more that 5 seconds). XCTest framework would fail the test after 5 seconds of waiting.

wait(for: [expectation], timeout: 5)

Waiting for 5 seconds for a unit test seems too much… Even 1 second seems! Imagine we had 1200 unit tests with asynchronous expectations fulfilment and because of some mistake in our code all of them fail. The framework needs to wait for each of them for 1s. Math is like that:

1s * 1200 tests = 1200s = 20min

We would have to wait 20 minutes for results of our unit test suite execution. Feels like eternity… Even Swift apps compile faster nowadays… 😉

wait(for: [expectation], timeout: 0.1)

Typically the waiting time should be set to 100ms. But even 100ms is a bit too much. In the case of failing tests we would have to wait for 2 minutes…

100ms * 1200 tests = 120s = 2 minutes

No control

But the main drawback is that we have NO CONTROL over DispatchQueue.main singleton used. We don’t know how much time the execution of our code will take. But we can regain the control. How? By running code synchronously!

Synchronous Assertions

Let’s assume we have a MessageLoader class that performs some operations on a background DispatchQueue:

class MessageLoader {
    let queue: DispatchQueue
    
    init(queue: DispatchQueue) {
        self.queue = queue
    }
    
    func load(_ completion: @escaping ([Message])->Void) {
        queue.async {
                        var messages: [Message] = []
                        //NOTE: fetches messages in background
                        completion(messages)
        }
    }
}

In this approach we cannot test in other way than using an XCTestExpectation that completion closure gets called after our function finishes doing stuff.

We can create a Dispatching protocol that declares a single method – dispatch(:).

protocol Dispatching {
    func dispatch(_ work: @escaping ()->Void)
}

Let’s create a Dispatcher class that gets initialised with a queue. It will serve as a superclass for other dispatchers.

class Dispatcher {
    let queue: DispatchQueue
    
    init(queue: DispatchQueue) {
        self.queue = queue
    }
}

We can hide async dispatch of a job to a DispatchQueue by creating an async dispatcher – let’s call it AsyncQueue. The class conforms to our Dispatching protocol and performs action asynchronously on a queue that an instance is initialised with.

class AsyncQueue: Dispatcher {} //inheritance gives an initialiser with a queue

extension AsyncQueue: Dispatching {
    func dispatch(_ work: @escaping ()->Void) {
        queue.async(execute: work) //IMPORTANT!
    }
}

We can also create a SyncQueue that would dispatch a job synchronously on a queue.

class SyncQueue: Dispatcher {} //inheritance gives an initialiser with a queue

extension SyncQueue: Dispatching {
    func dispatch(_ work: @escaping ()->Void) {
        queue.sync(execute: work) //IMPORTANT!
    }
}

Ok, but what is this all for? We wanted to test synchronously! So we need to upgrade our MessageLoader to use the Dispatching queue to perform a job.

class MessageLoader {
    let queue: Dispatching
    
    init(queue: Dispatching) {
        self.queue = queue
    }
    
    func load(_ callback: @escaping([Message]) -> Void) {
        queue.dispatch { //NEW!
            var messages: [Message] = []
            //TODO: fetch messages in background
            completion(messages)
        }
    }
}

Instead of using sync or async method on a DispatchQueue we call dispatch on our Dispatching type. How to test synchronously that completion closure gets called? We just need to initialise MessageLoader with a SyncQueue.

Before that, let’s create some queues just like DispatchQueue gives access to commonly used queues:

extension SyncQueue {
    static let main: SyncQueue = SyncQueue(queue: .main)
    static let global: SyncQueue = SyncQueue(queue: .global())
    static let background: SyncQueue = SyncQueue(queue: .global(qos: .background))
}

extension AsyncQueue {
    static let main: AsyncQueue = AsyncQueue(queue: .main)
    static let global: AsyncQueue = AsyncQueue(queue: .global())
    static let background: AsyncQueue = AsyncQueue(queue: .global(qos: .background))
}

Let’s write a unit test for our completion closure being called. We need to initialise MessageLoader with a background queue on which jobs are dispatched synchronously. Imagine we are writing a messaging app. Our product manager gave us a task to welcome a user with a “hello” message even if there are no other messages.

let welcome = Message(author: "The App", text: "Welcome in the app!")

So we need to assert that we have at least one message in the array given as an argument to the completion handler. We can create an array outside a completion closure and assign a value to it in the completion.

func testAtLeast1MessageOnLoad() {

    //Arrange
    var messages: [Message] = []
    let background = SyncQueue.background
    let loader = MessageLoader()
    loader.queue = background
               
    //Act
    loader.load { fetched in
        messages = fetched
    }
    
    //Assert
    XCTAssertFalse(messages.isEmpty)
}

Thanks to the synchronous nature of this testing approach we also benefit from it by enhancing test readability. We have clearly visible Arrange, Act and Assert phases.

TL;DR;(1) – making things clear

We don’t have to use the XCTestExpectation because our code executes synchronously with SyncQueue. Unit test is run on the main thread and then load(:) executes it’s job synchronously on a background thread. The sync dispatch means, that the calling thread waits until execution of the job dispatched on a background thread finishes.

Beware of the 🐕 deadlock!

OK, let’s refactor. Or make things more difficult… But look out for a deadlock!

Imagine we have a loader that dispatches work on a background queue but calls completion closure on the main queue. If we used DispatchQueue objects our code would look like this:

func load(_ completion: @escaping ([Message])->Void) {
    
    DispatchQueue.global().async {
        //fetch messages on a background thread
        
        DispatchQueue.main.async {
            completion([ ]) //on the main thread
        }
        
    }
 }

Again, as in previous example, we can use the Dispatching protocol to hide a use of a DispatchQueue.

class MessageLoader {
    let main: Dispatching
    let background: Dispatching
    
    init(main: Dispatching, background: Dispatching) {
        self.main = main
        self.background = background
    }
    
    func load(_ callback: @escaping([Message]) -> Void) {
    
        background.dispatch { //NEW!
            
            var messages = [ Message.welcome ]    
                    
            //TODO: fetch messages in background
            
            self.main.dispatch { //NEW!
                callback(messages)
            }
        }
    }
}

If we wanted to test that completion closure gets called and we didn’t pay attention to what queue we dispatch jobs during a unit test we might end up with a deadlock.

What is the deadlock? In a multi-threaded application it occurs when a thread enters a waiting state because a requested system resource is held by another waiting thread.

We want to test our new loader with a simple test that calls load(:) method on MessageLoader instance. We initialise our loader with SyncQueue.main and SyncQueue.background objects that perform work synchronously on the main queue and on a background queue respectively.

func testAtLeast1MessageOnLoad() {

    //Arrange
    var messages: [Message] = []
    let loader = MessageLoader(main: SyncQueue.main,
                               background: SyncQueue.background)
               
    //Act
    loader.load { fetched in
        messages = fetched
    }
    
    //Assert
    XCTAssertFalse(messages.isEmpty)
}

Just to remind you – unit test is run on the main thread and then load(:) executes it’s job synchronously on a background thread. When the job on the background thread finishes the method dispatches work synchronously to the main thread to call completion closure. Do you see an issue with this approach?

A synchronous dispatch means that a dispatching thread waits for a work to be finished on a thread it dispatches the job to. In the load(:) we dispatch work synchronously from the main thread to a background thread and then we perform another synchronous dispatch to the main thread. Because the previous synchronous dispatch is not finished and we try to do another synchronous dispatch to the same queue we end up with a deadlock.

How can we fix that? We need to use a different queue than main, e.g. SyncQueue.global 😉.

let loader = MessageLoader(main: SyncQueue.global,
                               background: SyncQueue.background)

And now we use threads as following and no deadlock occurs:

Summary a.k.a TL;DR;(2)

Asynchronous tests are unreadable, might give an overhead to test execution and we don’t have full control over them.

Using a protocol to “hide” dispatching jobs to different threads allows running tests synchronously. It gives us FULL CONTROL over unit tests execution and prevents us from waiting for too long in the case of their failure. We can also clearly see the Arrange, Act and Assert phases of a unit test.

You can check our sample code on GitHub.

Special thanks to Paweł Dudek for a review!

Links

]]>
http://swifting.io/blog/2018/03/03/50-synchronous-unit-tests/feed/ 4
#18 Do pure Swift objects have load() method? http://swifting.io/blog/2016/06/13/18-do-pure-swift-objects-have-load-method/?utm_source=rss&utm_medium=rss&utm_campaign=18-do-pure-swift-objects-have-load-method http://swifting.io/blog/2016/06/13/18-do-pure-swift-objects-have-load-method/#comments Mon, 13 Jun 2016 07:00:10 +0000 http://swifting.io/?p=486 I’m pretty sure this day, or actually the whole week, will be pretty exciting due to the start of WWDC 2016. Before that happens I’d like to share my finding from unit testing of one of my view controllers. If you’re not familiar with unit testing, check out our last issue Unit Test all the things!.

Unit test all the things!

We encourage you to test all the things 😉 ! But mainly interactions between objects. I also like to test communication of view controllers with their dependencies. The example below shows how a sample test could look like:

context("ViewController") {
    var sut: ViewController!
    var dependencyOfSut: Dependency!
    
    beforeEach {
        dependencyOfSut = MockDependency()
        sut = ViewController(dependency: dependencyOfSut)
    }
    
    describe("when view loads") {
        beforeEach {
            sut.viewDidLoad()
        }
        
        it("setups something") {
            expect(dependencyOfSut.setupSomethingCount).to(equal(1))
        }
        
    }
}

The snippet should be self-explanatory. If it isn’t, let me elucidate it a bit. This is a test of ViewController class, that is initialised with a Dependency object in beoforeEach‘s closure argument. Test checks what happens when viewDidLoad method gets called. It is expected that setupSomething method is called once when view loads.

Ok, but what else can we test?

We can check if appropriate buttons are set on navigationItem:

it("has right button") {

    expect(sut.navigationItem.rightBarButtonItem)
        .to(beAKindOf(UIBarButtonItem))
        
    expect(sut.navigationItem.rightBarButtonItem?.enabled)
        .to(beTrue())
        
}

We can also assert if view controller is UITableView’s or UICollectionView’s delegate:

expect(sut.tableView.delegate 
    as? MyViewController).to(equal(sut))
    
expect(sut.collectionView.delegate 
    as? MyViewController).to(equal(sut))

Another super important thing to check is if an appropriate action takes place after a tap on a button. Last week we migrated an app from Swift 2.1 to 2.2 and we didn’t have tests for navigation bar buttons. Buttons simply stopped working. If we had tests for their actions we would have noticed the bug before releasing demo version of our app to client. Of course a tester could assert that everything works correctly, but you need to have one. Unit test all the things. Really! 🙂

What has changed in Swift 2.2? Among other things the syntax for selectors.

The old way:

navigationItem.rightBarButtonItem =
        UIBarButtonItem(barButtonSystemItem: .Add, target: self,
                        action: "doSomething")

Code in Swift 2.2:

navigationItem.rightBarButtonItem =
        UIBarButtonItem(barButtonSystemItem: .Add, target: self,
                        action: #selector(doSomething))

BTW, if you don’t know what’s your Swift version in Xcode you can check it out with the xcrun swift -version command in Terminal.

Ok, but how to test if an action is connected to a button? Look at the snippet below. Imagine we had a Helper class that could perform a tap on a button (more about it in a moment). In assert part of your test you simply need to check if a desired action happened, just like before, by using method counters.

What if you wanted to check if a new view controller was shown after action? You need to use toEventually function that is able to asynchronously assert the expectation. Your expectation is to check if presented view controller of tested object is of certain kind:

describe("tap on the right button") {
    beforeEach {
        Helper.tap(sut.navigationItem.rightBarButtonItem)
    }
    
    it("shows my other view controller") {
        expect(sut.presentedViewController)
            .toEventually(beAKindOf(MyOtherViewController))
    }
}

The Helper class actually exists. It was typealiased in the snippet. I’ve actually called it UnitTestHelper and its contents is available in this gist.

class UnitTestHelper {
    
    class func tap(button: UIButton?) {}

    class func tap(button: UIBarButtonItem?) {}
        
    //...
    
}

The tap function uses UIApplication‘s sendAction:to:from:forEvent method for sending an action to button’s target.

If you want to see more samples of UIViewController unit tests, check this gist. It contains snippet for testing a view controller with a view model and a table view. Ok but let’s go to the main question of this post.

Do pure Swift objects have load method?!

NSObject instances/subclasses have load() class function that is

Invoked whenever a class or category is added to the Objective-C runtime;

–Apple’s documentation

In my recent project I used MVVM architecture, where every view controller has its own view model (a component that deals with business logic and formats data to be displayed). One of the desired interactions of view controller with its view model was that view controller, in viewDidLoad, calls load method on view model to load data to be displayed.

class MyViewModel {
    func load() {}
}

class MyViewController: UIViewController {
    let viewModel: MyViewModel
    init(viewModel: MyViewModel = MyViewModel()) {
        self.viewModel = viewModel
    }
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = UIColor.whiteColor()
        viewModel.load()
    }
}

In terms of unit test spec the desired behaviour was asserted like this:

describe("when view loads") {
     beforeEach {
        sut.viewDidLoad()
     }
     it("calls load on viewModel") {
        expect(viewModel.loadCount)
        .to(equal(1)) 
    }
}

I ran tests, they failed, which was ok at that point. After all I use BDD and I was in the red phase. Then I implemented the code, ran tests again and the aforementioned assertion failed again. I started scrutinising my code, but I didn’t find any flaw. What had possibly gone wrong? The assertion looked like this:


I couldn’t believe my eyes. How?! Pure Swift object with load method called before I called it? Do pure Swift objects have load method?! I scrutinised my code again, put some breakpoints here and there. Then I realised that this method gets called after I called viewDidLoad:

func loadView()

The view controller calls this method when its view property is requested but is currently nil.

–Apple’s documentation

Do you know what’s called after loadView? The viewDidLoad method. Hence load was called twice on viewModel. And why on Earth was the loadView called? Because I touch view property for the first time during test runtime:

override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = UIColor.whiteColor() //HERE view is touched for the first time during the unit test
        viewModel.load()
}

What’s the solution to this problem? Mine was to accept the loadCount to be greaterThan(0). Instead of calling viewDidLoad I could just access sut.view in beforeEach in order to call viewDidLoad only once. UPDATE July 9th 2016: Starting from iOS9 you can also use the loadViewIfNeeded method and remember not to touch view during initialisation 😉! Thx for Kakamil for the tip!

Maybe you have other solution to the problem, or have experienced other unit testing story. I encourage you to share them in comments :)!

TL; DR

Pure Swift objects don’t have load method.


]]>
http://swifting.io/blog/2016/06/13/18-do-pure-swift-objects-have-load-method/feed/ 4
#17 Unit test all the things! http://swifting.io/blog/2016/06/06/17-unit-test-all-the-things/?utm_source=rss&utm_medium=rss&utm_campaign=17-unit-test-all-the-things http://swifting.io/blog/2016/06/06/17-unit-test-all-the-things/#respond Mon, 06 Jun 2016 06:58:49 +0000 http://swifting.io/?p=404 There is always a half

When I was at the 3rd year of my BEng studies I had a chance to visit Turin, Italy, and to take part in Erasmus student’s exchange programme. At Politechnico di Torino I attended "Operating Systems" course on which the lecturer used to ask participants wether they had ever done some programming in C or not, had used selected linux commands and etc. Then he would count all the hands in the air to check how many of us had had a knowledge about the certain topic. No matter how many hands were up he always wittingly said a half.

Recently I did the same on iOS Developers Meetup in Poznań, Poland. And of course, the result was that a half of attendants had done the activity I asked about 😉. I wonder to which group you belong. So now I’m asking you:

Are you a person who has never written a unit test in Xcode?

If not, maybe you are a person who would answer positively to this question:

Have you 🙊ever written a unit test in Xcode?

Regardless of your answer, this post is for you 💡! It will cover basic topics in BDD🔮, explain my Swift toolset🔧🔨 for unit testing and summarise benefits🍓 of performing unit tests in your project.

3 types of programmers

When I started my professional career I didn’t know what unit tests are. But after a few years I can easily point out three groups of programmers.

There is a small group of unit test lovers who say that unit testing is cool ❤️ and they couldn’t live without testing all the things. Really, a really small group!

There is also the majority that says that real men 👨🏻 test on PRODUCTION. Kinda risky but good luck for them 🍀!

And there are many, especially in iOS world, that don’t know how the 🙊 they can start doing it? I was one of them two years ago. Thanks to interesting people I have met now I know …

… WT🙊 a unit test is?

If you have ever tried out searching on Wikipedia what a unit test is you would be surprised what its standard bla, bla, bla says.

In computer programming, unit testing is a software testing method by which individual units of source code, sets of one or more computer program modules together with associated control data, usage procedures, and operating procedures, are tested to determine whether they are fit for use.

–Wikipedia

It didn’t illuminate me a bit. So based on my experience I have created my own definition:

code that tests source code

–Maciej Piotrowski


To understand the definition🔮 profoundly we have to understand a few terms at first.

What’s an app?

There are a few equations that can define an app. Let’s look at them.

First of all we should know that the app is a set of behaviours. By behaviour one can understand certain actions that take place, e.g. when user taps a button in our app it shows another view controller, etc.

  • App = Set(behaviours😄😭)

Application consists of components. If not, you’re probably doing it wrong 😉!

  • App = component 🎁 + … + component 🎁

Those components interact with each other in order to fulfil a certain behaviour.

  • component ← interaction 👏🏻 → component

Our code that tests source code can test that particular interaction between components took place. This is called behavioural unit testing.

  • unit test = checks(🎁interaction 👏🏻🎁) == ✅

Of course we can also test function’s output when sticking to BDD. BDD!? Wait. What’s BDD?

TDD & BDD

BDD stands for Behaviour Driven Development. It’s built upon TDD, Test Driven Development. Basic rule of TDD is:

Don’t you dare writing code before writing a test for it!

Namely, you have to write testing code to assert what component code should do and then implement the component itself. TDD flow distinguishes three steps: RED, GREEN, REFACTOR.


What’s different in BDD? Behaviour-driven development tells that tests of any unit of software should be specified in terms of the desired behaviour of the unit. Basically, a behaviour is what a component does when a certain action (i.e. method call) takes place. When you call a function it probably communicates and interacts with other components. When writing a unit test we can just test if an interaction took place (as mentioned earlier).

Behaviours😄😭

Imagine we have to write a component which deals with CoreBluetooth API. Let’s call it BluetoothModule. It’s main responsibility is to start and stop advertising Bluetooth Services. It will support only that, hence our iPhone would become a Bluetooth Low Energy Peripheral (information for curious readers)

Ok, so let’s write down our expected Behaviours 😄😭.

"BluetoothModule"
            "when turned on"
                "advertises services"
    
            "when turned off"
                "doesn't advertise services"

What the 🙊 spec?!

I kinda looks like a specification 📋 of our source code. There is a nice BDD framework to write testing code with a 🔮DSL. It’s called Quick and we can create specs with it! :). An excerpt from a spec would looks like this:

context("BluetoothModule") {
    describe("when turned on") {
        it("advertises services") {}
    }
    describe("when turned off") {
        it("doesn't advertise services") {}
    }
}

Normally a spec contains assertions 🔔 (testing code). Quick has a counterpart, called Nimble – it has a set of matchers handy in writing expectations. More on that in a moment, but before let’s have a look on how to write a QuickSpeck:

import Quick
import Nimble
@testable import <#Module#>
class <#TestedClass#>Spec: QuickSpec {
    override func spec() {
        //🔮Magic goes here 🙈🙉🙊
    }
}

First and second lines import Quick (BDD DSL) and Nimble (matchers) frameworks. The @testable directive introduced in Swift 2.0 let’s us import our app’s module in a ‘testable’ manner, i.e. we can access components’ internal methods and properties.

All tests should be contained in spec() method overriden in QuickSpec subclass. Inside the method we use Quick DSL to describe component’s specification and assert its behaviours.

Arrange, Act, Assert

Ok, but now a question should have already crossed your mind – how to write asserting (testing) code?

Every test consists of three phases:
– Arrange – arrangement of our ‘scene’, i.e. initialisation of tested object and its dependencies (components that it interacts with)
– Act – execution of an action, i.e. a method call on a tested object
– Assert – actual testing code, an assertion that checks that the expected behaviour/interaction took place

Let’s assume our component to be tested has this simple interface:

class BluetoothModule {
    init(peripheralManager: CBPeripheralManagerProtocol)
    func turnOn()
    func turnOff()
}

It has two methods and takes an object that implements CBPeripheralManagerProtocol as its dependency. Why CBPeripheralManagerProtocol and not just CBPeripheralManager? More will be explained in I wish we had mockingjays 🐦 section of this article. We can arrange testing "scene" for with mock object as in the snippet below:

class BluetoothModuleSpec: QuickSpec {
    override func spec() {
        context("BluetoothModule") { //i.e. newly initialised
            var sut: BluetoothModule!
            var peripheralManager: CBPeripheralManagerProtocol!
            beforeEach {
                peripheralManager = MockPeripheralManager()
                sut = BluetoothModule(peripheralManager: peripheralManager)
            }
            afterEach {
                peripheralManager = nil
                sut = nil
            }
        }
    }
}

Quick gives us a few functions we can use to arrange a "scene":

  • context – description of object’s state (e.g. object is newly initialised), takes String with a description and a closure as argument
  • beforeEach – takes a closure argument to setup local variables (corresponds XCTest’s setup)
  • afterEach – takes a closure argument to cleanup local variables (corresponds XCTest’s tearDown)

When we have all objects set up the Act phase approaches. It’s the phase in which we invoke certain methods on the tested object:

context("BluetoothModule") {
    //...
    describe("when turned on") {
        beforeEach {
            sut.turnOn()
        }
    }
    describe("when turned off") {
        beforeEach {
            sut.turnOff()
        }
    }
}

Again, Quick gives us a few functions we can use to perform actions on the arranged object:

  • describe – used for description of an action performed on the subject, takes String with a description and a closure as argument
  • beforeEach – used for performing an action on the sut (subject of unit testing a.k.a. system/subject under test – choose what suits you best)

The last, but not least, is Assert phase. In this part we write actual testing code (code that tests source code):

context("BluetoothModule") {
    describe("when turned on") {
        //...
        it("advertises services") {
            expect(peripheralManager.isAdvertising)
                .to(beTrue())
        }
    }    
    
    describe("when turned off") {
        //...
        it("advertises services") {
            expect(peripheralManager.isAdvertising)
                .to(beFalse())
        }
    }    
}

Quick comes in handy with it function – takes a String with a description of desired outcome of a test and closure with a test of an expected behaviour. Nimble gives as a way to write some expectations. But what and how to expect?

What and how to 🙊 expect?

Of course you can Expect the unexpected. Sometimes test outcome will surprise you remarkably. In the next issue I will write about one of surprises I had with my testing code.

In our code that tests source code we use expect() method from Nimble 💥 framework, which also provides a number of matchers to fulfil the expectation. Matchers are used in it blocks to assert the expectation ✅. Let’s have a look at some example expectations:

expect(sut.something)
    .to(equal(5))
    
expect(sut.somethingDifferent)
    .toNot(beAKindOf(MyClass))
    
expect(sut.somethingElse)
    .toEventually(beNil())

As you can easily see, the expect() function takes as an argument an expression to be evaluated by using a matcher provided in to*() function. We can expect e.g. a property on our tested object to equal some value, to be a certain kind of class, or to eventually be Optional(.None) 😉.

The to() and toNot() functions are rather straight forward – they test the actual value of expectation with the matcher. The toEventually() function is used for asynchronous tests. It continuously checks the assertion at each pollInterval until the timeout is reached.

Hey, if an object needs to be checked ✅ with equality with other object, we need to implement Equatable protocol! To do so, we just need to implement the ==() operator:

extension MyClass: Equatable {}

func ==(lhs: MyClass, rhs: MyClass) -> Bool {
    return lhs.propertyX == rhs.propertyX 
    && lhs.propertyY == rhs.propertyY
    && lhs.propertyZ == rhs.propertyZ 
}

When the above approach is suitable for structs, for classes we could be lazier and compare arguments’ ObjectIdetifier() instead of comparing different properties:

extension MyClass: Equatable {}

func ==(lhs: MyClass, rhs: MyClass) -> Bool {
    return ObjectIdetifier(lhs) == ObjectIdentifier(rhs)
}

If you happen to be a super lazy person, this approach is for you. Just allow your class to inherit from NSObject, or probably any Cocoa/CocoaTouch class, because all of them are NSObject subclasses, and you get equality comparison for free🎁!

class class1: NSObject {}
class class2: NSObject {}

let c1 = class1()
let c2 = class2()

it("should not be equal") {
    expect(c1).toNot(equal(c2))
}

It’s important to implement Equatable or use the approach for super lazy people, if we do not, we won’t be able to compare objects for equality with Nimble and will get a confusing message from compiler 😢.


I wish we had mockingjays 🐦

Remember our BluetoothModule object? It takes in init() an object that implements CBPeripheralManagerProtocol as its dependency. There are at least two reasons why the dependency is declared in terms of protocol instead of pure CBPeripheralManager instance.

In unit testing we want to have full control over dependencies injected to tested object. To have it we inject test doubles as dependencies. We distinguish a few types of test doubles:

  • stub – fakes a response to method call
  • mock – allows to check if a call was performed
  • partial mock – actual object altered a bit (some responses faked, some not)

W🙊W. Out of the box mocks are not possible in Swift

In Objective-C we were able to easily mock objects thanks to its dynamism and access to run time. But Swift is currently read-only 📖. There is no way to change class types & objects at run time 😢. So …

Communicate with objects through protocols:

protocol CBPeripheralManagerProtocol: class {
    
    //...
    weak var delegate: CBPeripheralManagerDelegate { get set }
    
    func startAdvertising()
    func stopAdvertising()
}

If you don’t you will end up with inheritance and partial mocks, which is not recommended. Bad things can happen when you inject real CBPeripheralManager instances or partial mocks made by subclassing it.

So we have an interface defined by protocol. Why not to implement it in our Mock?

class MockCBPeripheralManager: CBPeripheralManagerProtocol {
    
    //...
    var startAdvertisingCount = 0
    
    func startAdvertising() {
        startAdvertisingCount += 1
    }     
    
}

Of course we can leave some methods or properties with an empty implementation. Implement just those you need. E.g. to check if a method was called add a method call counter and assert it’s value in a test:

context("BluetoothModule") {
    describe("when turned on") {
        //...
        
        it("advertises services") {
            expect(peripheralManager.startAdvertisingCount)
                .to(equal(1))
        }
        
    }    
}

What is it all f🙊r ?

Unit testing is still not common in iOS development. If someone asks TDD/BDD gurus about how much time they spend on writing tests when developing an app, they answer a half⌛️. That’s a lot, isn’t it? But it pays off in the end by having:

  • better understanding 🔍 a codebase
  • 📈 well thought architecture
  • properly documented assumptions 🔮
  • no fear of making changes 💥
  • getting to know 🍏 Apple frameworks better
  • ❤️ fun !!! ❤️

If you don’t know how to start, on June 18th Mobile Academy organises Swift TDD training in Poznań. Let’s meet there :)!

TL; DR

The time to start writing unit tests❤️ is now!


Resources

]]>
http://swifting.io/blog/2016/06/06/17-unit-test-all-the-things/feed/ 0