I sometimes find myself writing unit tests that is testing the same function for different examples of data. Instead of repeating almost the same code over and over again, I like to parameterize those tests and make them data driven. 

However, when I started coding Swift and making unit testing with Quick and Nimble I couldn’t immediately get that same approach to work. This caused not only sadness in my life, but a lot of repeated code that I really thought it shouldn’t be necessary.

Yesterday, I found myself in the situation, again, where almost the same code would duplicated for every type of message that my code should handle. Then I decided to go to the bottom with why the data driven approach would not work with Quick and with a bit of luck, create a solution and a PR.

So, I started a swift project for the purpose of isolating the problem with the smallest possible example. I didn’t take me long to write a small data driven set of tests that I had not been able to get working a year ago. And imagine my surprise, when my little data driven test suite just ran straight off the bat. The problem wasn’t there anymore.

I thought to myself, that maybe it had been working all the time and I had just messed up before. Perhaps that really is the case, but that doesn’t change the fact that I had not been able to find any examples on the internet where Quick/Nimble was used in a data driven way, including the official documentation. Then I tried the approach in my project code, in a slightly different way, and then it didn’t work. Adapting my project code to follow the exact same pattern as my isolated example, I finally got data driven unit tests to work in my project. 

Because it was not a completely hassle free experience and since there was not much to find on the net about it, I thought I should share an example. So, here is an example using Swift 4.2, Quick 2.0 and Nimble 8.0. I hope you will have good use for it!

Data driven unit tests in Quick/Nimble

import Quick
import Nimble

class QuickDataDrivenTests: QuickSpec {
    override func spec() {
        describe("a function") {
            let specified_examples : [(input: Int, output: Int)] = [
                (input: 0, output: 0),
                (input: 1, output: 2),
                (input: 2, output: 4)
            ]
            specified_examples.forEach { (input: Int, output: Int) in
                context("for input \(input) and output \(output) ") {
                    var for_each_executed_with_input : Int?
                    beforeEach {
                        for_each_executed_with_input = input
                    }
                    it("should execute the beforeEach") {
                        expect(for_each_executed_with_input).toNot(beNil())
                    }
                    it("should execute the before each with each entry") {
                        expect(for_each_executed_with_input).to(equal(input))
                    }
                    it("should double the input for output") {
                        expect(double(input)).to(equal(output))
                    }
                }
            }
        }
    }
}

func double(_ value : Int) -> Int { return 2*value }

Some pitfalls

There are some pitfalls, that I stumbled upon that might cause you an issue.

Duplicate test names.

Remember to use the data to make the strings in the context, and it unique. For example by using the Swift string interpolation like

context("for input \(input) and output \(output)

Your tests are not discovered

It seems that the forEach block must have the tests in at least one context to be discovered and later executed. 

Simple when you know it

As you see the example is pretty straight forward and simple, once you get all the parts in. I hope this will help many swift programmers reduce their boilerplating in Swift unit tests and encourage even more unit testing 🙂