June 28, 2022

Write Parameterized Tests in ApprovalTests.Swift by Transforming Sequences

Click to play

I’m Jon Reid, one of the maintainers of ApprovalTests.Swift. Today, we’re going to talk about transforming sequences, that is, lists of values.

If you haven’t already seen the episode on verifying arrays, I suggest you check that out, because it lays the foundation for what we’re about to explore.

Review: verifyAll

Last time, we were verifying a list of the names of the dwarves from The Hobbit. We started with a simple verifyAll to verify each array index and its corresponding value. Then we switched from using a label string to a labeler closure. This let us look at the data as a sequence of values without indices.

try Approvals.verifyAll(names, labeler: { $0 })

Here’s what it looks like when the labeler just passes on the data, and there’s a mismatch in the list.

Without indices, addition of new name pops out

(I’ll remove the mismatch so we get back to a passing test.)

Transforming the Sequence

Now let’s say instead of sorting the names, we want to transform the names. That is, instead of reordering the sequence, we want to use each value in the sequence as input to a function. For example, let’s say we want to test how strings are uppercased.

Let’s start by duplicating our first test. Instead of testing sorting of names, we’ll test uppercased. Let’s remove the sorting. And for each name, let’s transform it so it’s uppercased.

func test_uppercased() throws {
    let names = TheHobbit.dwarves
    try Approvals.verifyAll(
        names,
        labeler: { $0.uppercased() }
    )
}

When we run this test, you can see that we get all the names converted to uppercase.

BALIN, DWALIN, KILI, etc.

The problem is, this doesn’t show you what was getting uppercased into what. There’s a loss of information.

Adding Information To Show Input and Output

Let’s add that information into the labeler closure so we can see what’s going on. Let’s use string interpolation, with the input on the left, and the uppercased version on the right.

try Approvals.verifyAll(
    names,
    labeler: { "\($0) -> \($0.uppercased())" }
)

Now when we run this, it shows us what came in and what came out.

Dwalin -> DWALIN, Balin -> BALIN, Kili -> KILI, etc.

And this gives us a lot more information, which will be helpful if this test were ever to fail. Let’s approve this, and now the test passes.

Conclusion: Simple Parameterized Tests!

So while XCTest still lacks support for parameterized tests, it’s easy to have them with ApprovalTests.Swift. Just use verifyAll with a labeler closure. This can improve the power of your tests, and at the same time reduce the number of tests you have to maintain.

While XCTest still lacks support for parameterized tests, it’s easy to have them with ApprovalTests.Swift

Click to Tweet

If you have any questions about ApprovalTests.Swift, please ask in the comments below. You can also send a tweet to me on Twitter, @qcoding, and add the hashtag #ApprovalTests.

Like Magic: How to Wrangle Legacy Code with Combination Approvals

Jon Reid

About the author

Programming was fun when I was a kid. But working in Silicon Valley, I saw poor code lead to fear, with real human costs. Looking for ways to make my life better, I learned about Extreme Programming, including unit testing, test-driven development (TDD), and refactoring. Programming became fun again! I've now been doing TDD in Apple environments for 20 years. I'm committed to software crafting as a discipline, hoping we can all reach greater effectiveness and joy.

{"email":"Email address invalid","url":"Website address invalid","required":"Required field missing"}

Never miss a good story!

Want to make sure you get notified when I release my next article or video? Then sign up here to subscribe to my newsletter. Plus, you’ll get access to the test-oriented code snippets I use every day!

>