mokacoding

unit and acceptance testing, automation, productivity

Enhancing XCTest test cases with Nimble matchers

Here on mokacoding we've talked a number of times about the benefits of the xSpec style when writing unit tests.

There is a catch though. Because XCTest does not provide a DSL for xSpec style testing we need to use a third party library to write in this wonderful style. The best xSpec frameworks for Objective-C are, in alphabetical order, Cedar, Kiwi, and Specta, while on Swift Quick is the one with the biggest traction.

Unfortunately the iOS world doesn't have a strong testing culture, yet, and is not uncommon to join a team where unit testing had to be introduced from scratch.

In such case dropping a framework combo like Specta + Expecta + OCMock might be too much for the team to handle in one go. It might be better to stick with XCTest and work on the team testing workflow instead.

If you decide to go with this approach you'll soon discover two things:

  • The xUnit style is not as bad as you remembered.
  • Everybody only uses XCTAssert and XCTAssertEquals.

That's the thing, the assertion provided by XCTest are not powerful enough, and way to verbose.

Let me introduce you to Nimble a matcher framework for Swift (and Objective-C) that you can easily integrate in your XCTestCases to have a way nicer and easy to write syntax.

This is how some usual assertion look when written using Nimble

XCTAssertEqual(anInt, 42)

expect(anInt) == 42
XCTAssert(aBool)

expect(aBool).to(beTrue())
XCTAssertFalse(anotherBool)

expect(anotherBool).to(beFalsy())
XCTAssertEqual(aString, "expected value")

expect(aString) == "expected value"

They read quite better, and in particular the relationship between actual and expected value is explicit. Plus, how neat is it to use == to test for equality? 😎

And it doesn't end here, Nimble provide syntactic candies like:

Negative forms

expect(batman.name).toNot(equal("Peter Parker"))
expect(ironman.name).notTo(equal("Barry Allen"))
expect(greenArrow.name) != "Matt Murdok"

Math comparisons

expect(actual).to(beLessThan(expected))
expect(actual) < expected

expect(actual).to(beLessThanOrEqualTo(expected))
expect(actual) <= expected

expect(actual).to(beGreaterThan(expected))
expect(actual) > expected

expect(actual).to(beGreaterThanOrEqualTo(expected))
expect(actual) >= expected

Range comparisons

expect(10.01).to(beCloseTo(10, within: 0.1))
expect(actual) ≈ (expected, delta)
expect(actual) == expected ± delta

Async expectations

expect(systemUnderTest.someState).toEventually(beTruthy(), timeout: 3)

Swift 2 error handling

expect{ try somethingThatThrows()  }.to(throwError())

You can learn about all the awesome matchers that Nimble provides out of the box in the README, and if you need more expressive power you can even write your own.

Conclusion

Nimble is a great Swift matcher framework. Integrating it with vanilla XCTest suites is easy and frictionless.

By providing an easy to use and powerful syntax for expectations you can help your team adopt unit testing and be productive, with a very smooth learning curve.

If you want some alternatives, have a look to Expecta and OCHamcrest.

Want to share your experience with Nimble or with introducing testing and TDD is your workspace? Tweet me @mokagio, or leave a comment below.

Leave the codebase better than you found it.