Vague tests are clear tests

by Dan Cutting

Specificity is almost always a good thing.

But sometimes when unit testing, I need to pass dummy values to functions just to make the compiler happy.

I don’t care what the values are, but using specific literals can obscure the intent of the tests:

func test_stack_count() {

    let stack = Stack()

    stack.push("hello")
    stack.push("bye")
    stack.push("hello")

    XCTAssertEqual(3, stack.count)
}

This test checks that the number of things on a stack is correct after pushing some strings onto it.

Somebody coming across this test may have a few questions: is it important that these particular values are pushed? Is the duplication of "hello" significant?

In fact, it doesn’t matter what is pushed onto the stack, so this test shouldn’t introduce any doubt in the mind of the reader.

In such cases I often make a helper function:

func any() -> String {
    return "dummy"
}

Now I can clarify the intent of the test: it’s the action of pushing I care about, not the values I push:

func test_stack_push() {

    let stack = Stack()

    stack.push(any())
    stack.push(any())
    stack.push(any())

    XCTAssertEqual(3, stack.count)
}

I use the same trick for Int values, and indeed any other time I need a dummy value of a particular type:

func any() -> Int {
    return 99
}

func test_division() {

    let calculator = Calculator()

    XCTAssertThrowsError(try calculator.divide(any(), by: 0))
}

But what if I need a dummy String and a dummy Int? Won’t the two any() function names clash? Will I need to uglify them to anyString() and anyInt()?

Fortunately, Swift functions can be overloaded not only by their name and parameter types, but also by their return type. That means these functions can coexist:

func any() -> String {
    return "dummy"
}

func any() -> Int {
    return 99
}

So I can write tests that use any() wherever I don’t care about the value and it will do the right thing:

func test_ditto() {

    XCTAssertEqual("hihihi", ditto(string: "hi", times: 3))

    XCTAssertEqual("", ditto(string: any(), times: 0))

    XCTAssertEqual("", ditto(string: "", times: any()))
}

This trick is not confined to primitive Swift types either. I use it for all sorts of things, including my own complex types:

func any() -> URL {
    return URL(string: "http://example.com")!
}

func any() -> UIViewController {
    return UIViewController()
}

func any() -> ReportWriter {
    return FileReportWriter(filename: any())
}

Sometimes it’s better to be, you know, kind of vague-ish. Or whatever.

Hi, I'm Dan Cutting. I mainly write Swift for iOS but love working on Mac apps, web APIs, and programming languages too.

Follow me on Twitter, Micro.blog, or GitHub or subscribe to the feed.