Writing Future Proof Tests in iOS by Adding DSL

Photo of Piotr Szadkowski

Piotr Szadkowski

Updated Jan 5, 2023 • 4 min read
dsl_ios_tests

While writing tests, it’s important that we aim to test behaviour and not the implementation.

This helps us ship faster and prevents people from turning off the tests in the future due to low maintainability.

That’s why it’s worth adding an abstraction layer in the form of DSL (Domain Specific Language).

Saying that, let’s unwrap this phrase for our needs. We’ll do it by taking a look at an example. Think of a game with multiple players, at the end of which we want to present a table view with multiple winners.

Depending on implementation, we might directly have it as a part of the view controller, or have it as a part of a dedicated view. Let’s assume the first scenario:

final class WinnersViewControllerTests: XCTestCase {

func test_onLoad_showsAllWinners() {
// given:
let sut = makeSUT(for: [winnerOne, winnerTwo, winnerThree])

// when:
sut.loadViewIfNeeded()

// then:
XCTAssertEqual(sut.tableView.numberOfRows(inSection: 0), 3)
}

// MARK: Helpers

private func makeSUT(for winners: [Winner] = []) -> WinnersViewController {
return WinnersViewController(winners)
}

private var winnerOne: Winner { Winner(name: "winner 1") }
private var winnerTwo: Winner { Winner(name: "winner 2") }
private var winnerThree: Winner { Winner(name: "winner 3") }
}

The test looks fine. It has nicely separated Given, When, Then sections and covers our WinnersViewController as expected.

At the beginning, we said we wanted to test behavior and not implementation. However, where in the test can we test the implementation?

Take a look at this line:

XCTAssertEqual(sut.tableView.numberOfRows(inSection: 0), 3)

Wouldn’t it be nicer if we could just write:

XCTAssertEqual(sut.winnersDisplayed, 3)

How can we do this?

Simply add an extension at the bottom of the test file.

private extension WinnersViewController {
var winnersDisplayed: Int {
tableView.numberOfRows(inSection: 0)
}
}

If you have the intuition that we should also do something with this magic “0” number, that’s very good! We can get one step further and write inside the extension:

var winnersSection: Int { 0 }

Our example above now looks like:

private extension WinnersViewController {
var winnersDisplayed: Int {
tableView.numberOfRows(inSection: winnersSection)
}
}

Now, this is what we call a domain specific language (DSL). We orient our thinking inside the test not for the current UITableView implementation but rather on the expected business behaviour, which is to display the winners.

What’s more, if in the future we would like to change the collection view display because of new design requirements, we just need to update this small property instead of, for example, checking the table view in all of our tests.

Great work, you just created your own DSL!

Create DSL in iOS tests

If you want to learn more about writing future proof tests in iOS by adding DSL check our sources Netguru iOS Good Practices. For further reading we recommend iOS Unit Testing by Example by Jon Reid and Essential Developer

Photo of Piotr Szadkowski

More posts by this author

Piotr Szadkowski

Piotr works as a Senior iOS Developer at Netguru.
Create impactful mobile apps  Expand reach and boost loyalty. Get started!

Read more on our Blog

Check out the knowledge base collected and distilled by experienced professionals.

We're Netguru

At Netguru we specialize in designing, building, shipping and scaling beautiful, usable products with blazing-fast efficiency.

Let's talk business