Robert Martin brought condemnation from the Swift community with his post The Dark Path. How dare he insult our wonderful new language? Clearly he’s a n00b who hasn’t done enough Swift programming.
Except that he’s no n00b — not even close. As Mark Seemann said,
Perhaps you disagree with @unclebobmartin (at times I do), but remember: he's been programming all of YOUR LIFE. Don't presume him ignorant.
— Mark Seemann (@ploeh) January 14, 2017
Let’s examine the controversial post as it relates to Swift — focusing on unit testing and TDD.
Let me call out a common response. It was even stated in Chris Eidhof’s calm and measured Types vs TDD: A Response. Chris argues that it’s not strong typing or TDD, but both-and. I agree. Robert Martin does, too, if you read his follow-up Types and Tests.
But Chris’s article included a statement I disagree with. It’s something I heard from quite a few people, across Twitter and at work.
A type checker actually does testing for you. It’s not a replacement for TDD, but it allows you to completely get rid of a whole bunch of tests. For example, if you define a method
foothat returns an
Int, you can be sure it will only return
null, not anything else. No need to write a test.
In my experience, I haven’t found this to be true.
This may surprise you, especially if you know my enthusiasm for TDD. Why wouldn’t I write tests guaranteeing the return type?
Because most of the time, the “assert” section of a unit test is an equality check. It’s a simple way to say, “These are the property values I expect.” Having the same type is an implicit detail.
In fact, all that matters is that the object’s notion of equality is satisfied. So it could even be a different type! Conceptually, we’re just comparing the object’s properties. “When I send this object a particular message, do I get the expected result?”
There are even times when testing for equality is over-specification. In some situations, we care only about one property, not all of them. Both OCHamcrest and Swift Hamcrest support hasProperty matching. Even without Hamcrest, it’s why I recommend that Swift mock objects shouldn’t test for equality, but for an arbitrary predicate.
Over-specified tests are fragile tests. I usually don’t think about testing for a particular type. I’m even watchful when testing using equality.
Like any rule-of-thumb, there are exceptions to the rule. I can think of a common case when I do test for a particular type. When I write unit tests around push navigation, I check:
pushViewController(_:animated:)with the expected type of view controller?
That’s the only common scenario in which I test the type: when there’s some sort of hand-off to a framework, where the handed-off object is a subclass.
Curiously, in such scenarios, strong typing doesn’t reduce test scope! The framework method signature only knows about the superclass, but the actual object is a subclass.
nil arguments, though, or
nil return values?”
What about them? If I need them, I specify them in tests. If I don’t, I don’t.
nil was rarely ever a problem. Basically, it provides an automatic implementation of the Null Object Pattern.
Swift forces me to consider
nil for every Optional. This is usually good: it reminds me, “Oh, I may need more tests here.”
But it doesn’t decrease the tests.
Strict typing has noticeably slowed my TDD. This is in contrast to Objective-C’s hinted-but-still-duck typing.
Why? It makes it harder to substitute fakes. There’s no way to say, “You expect type X. But I’m only testing. Could you give this object a pass? It’ll handle all the messages you need.”
Swift’s current inability to do so works against easy testing.
Perhaps some folks who are clever with compilers can find a workaround, kind of like
@testable. Given Apple’s track record with unit testing support, we’ll need clever people from the community.
But I’m not holding my breath, and I can’t wait. Neither should you. Here’s my plea:
Swift makes TDD harder. Don’t let that stop you from doing TDD.
The benefits of TDD are so strong, it’s worth pressing through the learning curve. Even when a particular language makes that curve harder. (Hey, I learned TDD in C++.)
Don’t give up. It’s worth it.
I’ve been doing TDD for some time now. As far as I can tell, Swift’s strict typing hasn’t reduced the number of tests I write. Not by a single test.
Like Chris Eidhof, and even like Robert Martin, I appreciate type-checking. I value early feedback from the compiler. But don’t ever mistake “it satisfies the compiler” for “it meets my requirements”. Type-checking can ensure that you’re getting the right type. Ah, but unit testing. Unit testing can ensure that those types are the right values.
So yeah, it’s both-and. The problem is, one is interfering with the other. It’s unit tests that are non-negotiable. I do hope Swift might evolve to be more tolerant of simple testing.
Agree? Disagree? Please leave your comments below.
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 Design Patterns, Refactoring, and Test-Driven Development (TDD). Programming became fun again! I've now been doing TDD in Apple environments for 18 years. I'm committed to software crafting as a discipline, hoping we can all reach greater effectiveness and joy.
Please log in again. The login page will open in a new tab. After logging in you can close it and return to this page.