Here’s the text of a lightning-talk I gave at the beginning of 2016 at one of SafetyCulture’s Townsville company gatherings. I really liked it and thought I should share it here for posterity.
My talk today is on Unit Testing, kind of a boring topic I realise.
In the world of iOS unit testing doesn’t come naturally, It’s uncommon to find an iOS engineer with lots of unit testing experience. And yet every iOS developer here who has given it a go, has been amazed and how much it helps them. when writing new code.
So i’m wondering about that gap between the unit testing the chore and the epiphany of how helpful unit testing is. And this is my crack at bridging the gap.
I notice that when I talk about unit testing generally I always focus on the what and the how. What to test, what shouldn’t be in a unit test, how to test classes that have dependancies, how to mock objects and things like HTTP requests.
And mostly complaining about how the unit tests are always failing, needing maintenance, are out of date, or test very little.
How they take too long to write, or how, once automated testing is running, no one can merge a PR until all tests pass. Because I think that’s what happens when you focus on the what and the how. Unit tests become a chore.
It’s not a part of the programming you’re doing, it’s the bit you tack onto the end, that you didn’t estimate enough time for. Unit tests are a hassle, a pain in the arse, a velocity-sapping, slog.
The what and how tend to get entangled in procedure. it gets in the way of working out why your even doing it. Other than I have to write some and they have to pass to get my PR merged.
I think if you truely understand why you’re writing unit tests, all the rest follows. All the things you want to put in a test, all the ways you want to test it all become easier through the lens of knowing why.
And sure unit testing is helpful while you’re writing new code. Writing or refactoring code in a complex app can be terrifying. Adding tests along the way helps you make sure you’re on the right track, spot edge cases and so on sure.
But it’s not all about you!
Most defects are introduced during coding, but the least number of defects are found during coding. There’s only one reason to write unit tests, the only reason that matters.
You have to do it for love.
Unit tests are a declaration of love for your fellow engineers. They say I care about you, man, and the work you’re about to do. Don’t worry I got your back.
Unit tests are a declaration of love for the product. Unit tests are a declaration of love for SafetyCulture. What happens when you have love in your unit tests?
You start to think differently about your code , you start to see it from the other engineers’ perspective. You think about how to make it more testable, you think about all the possible states your code can get into.
You think about how someone else might approach using your API. You write your tests in a way that demonstrate how it should work, how it can fail, what can go wrong when it does.
You can demonstrate how your code will behave in a positive state, a negative state, and a tripping-balls-what-hell-is-going-on state.
When you go to work on a codebase with love in its unit tests you know you can rely on them to show you how it’s all supposed to work and reassure you that you haven’t broken anything.
You can bravely take on those big refactoring jobs, knowing your team is behind you all the way. Once you know why your writing those unit tests, it guides you on the implementation.
You worry about the time it takes to run: you want to make sure the next engineer can run them as often as they need to feel secure.
You avoid writing fragile tests that rely on timing, on application state, or UI state – anything that could randomly fail if conditions aren’t right – because that creates confusion, and uncertainty.
And you care that the engineer running your tests is not left confounded, wondering why the tests are failing 10% of the time.
You use mocking or stubbing only for what’s necessary, because overuse ties the unit tests too much to the implementation details of mocked classes. You don’t want to make your mate rewrite all your tests because some unrelated implementation detail has changed.
You avoid making persistent state changes you don’t want your unit tests leaving behind a mess someone else has to clean up.
You make sure to be as descriptive as you can about what’s going on in your tests and why the expected output is legit.
You make sure your tests actually test something useful.
Your avoid torturing your code (by breaking it into smaller and smaller parts until it’s perfectly testable, all dependancies are explicit, code coverage is very high, but understandability is very low) because you know you’re writing tests for you fellow human engineers, not writing tests for robots to run.
Even though we might automate them.
You write tests with love, because you’re not writing them for yourself, You’re writing them for the next engineer, because you care that they succeed.
Inevitably, 6 months down the track, that next engineer turns out to be you. And you can say, thanks past me, you’re weren’t such a jerk after all.