Unit Testing for the One-Person Shop - A Response

Unit Testing for the One-Person Shop - A Response

Recently, while catching up on episodes of one of my favorite podcasts (Under The Radar with Marco Arment and David Smith), there was a question from a listener (@b1ueskyz) about their thoughts on unit-testing for a single-person development shop, is it worth it, and how to start.

I need to preface my response to this tweet and their comments first by saying that I have the utmost respect for both of the hosts, and have been a dedicated listener to their podcasts since the days of now-retired Build & Analyze and Developing Perspective, and now their current popular tech podcasts, Accidental Tech Podcast and Under The Radar. I also have purchased many of their independently released apps, and use them daily. These two are fantastic developers, and such an amazing resource for sharing their expertise and knowledge so that the community can be better and stronger.

I do not intend for this piece to be negative towards either host. I do, however, have some thoughts to share that I feel were not well-represented in the response to the person asking the question. The person asking the question was wondering how to get started and if it is worth it, and I say yes, it is very worth it. In this post I’ll list out issues I have with what the hosts said, but also list reasons why I think testing is valuable. Just the fact that he asked the question means he has a desire to learn, and wants to know where to start.

This may be a long article for a simple question.

TL;DR Go for it. Or listen to the episode of Concepts In Code with me, Hank Turowski, and Joe Masilotti as we talk about testing.

1. Unit Testing Is Not Just For Life-or-Death Situations

Here, David answered the question stating that unit testing can be helpful where the cost of having a bug would be too great (with which I agree).

Filght Attendants, Prepare for Takeoff

The ensuing first example was if you were writing software to control a plane that is flying through the air, then you need some unit testing. Sure, that’s true, I’d say. In fact, I’d hope the software on the plane I’m on has been thoroughly tested. I disagree with how this answer was portrayed though, making it sound as if you don’t need unit testing unless it’s a life-or-death situation. Ever needed to ensure an algorithm you wrote works properly? Write some tests to expect certain output. Boom, there ya go. No lives in danger of being lost.

Repeated Work/Sanity Check

The second part of the answer was that of the feeling of hating to build the app twice. Indeed, unit tests can sometimes be a pain to manage. If things change significantly in the app’s design, logic, or general flow, tests can scream red at you and totally break your flow of concentration while you scurry to resolve the failing tests, whether that is by updating expectations, changing their behavior, adding new tests, or removing them altogether. While I agree it can be a pain, this type of checking is exactly what unit testing was meant for: to help protect you from accidental regressions. Why do you think accountants use double-entry bookkeeping? It’s not a guarantee that the books will be bug-free, but it is certainly a great sanity check, and also a pain in the keister if an error is found (but oh, what a great feeling that an error was found before you shipped to the public!).

My viewpoint of unit testing is that they are a tremendous resource for you (and your Future Self™) to give automated feedback on whether there are areas of your code that have changed inadvertently, or while trying to solve one problem you broke another, since the time you’d implemented a particular feature, algorithm, etc.

Don’t Feel You Have to Have 100% Coverage

Marco’s response was also not as positive, saying that if he had to write unit tests for everything he did, he would stop programming. When you are starting with testing, do not feel that you have to have 100% test coverage. Start with one test. Make it fail. Feel the red. Let it stare at you. Then write enough code to make the test pass. Then see the green. Give it a high-five. Give yourself a high-five. You made your first test pass. Tweet at me that you did it, and I’ll give you a high-five.

When writing tests, especially as you’re getting started, start with some basic tests to get comfortable with writing them. In fact, a great resource for practicing and getting comfortable with testing is exercism.io. But do not feel you need to write tests for absolutely everything. That, as Marco said, can be a waste of time and a royal pain.

So, then what are some good things to test?

I get this question a lot. Here are some things you can start with (but not limited to):
* An algorithm that you aren’t sure of the results yet for every case or logic path.
* A model class/struct/enum that has to return a value computed from other values (e.g., a fullName property is computed by interpolating firstName and lastName with a space in between).
* Making sure outlets or actions are hooked up properly in your storyboard to their respective view controller (if you’re doing Mac/iOS development, which the listener is).
* An extension you’ve created on NSDate/Date that will return the day 5 days from now, but also respect leap years and time zones.
* Basically anything you want to ensure proper output from (or side effects by) performing some action in your code (remember the three-step approach to testing: “Given/When/Then”, or “Given some input or initial state, when I perform some action, then I expect some output to be correct.”).
* Do not test UIKit, Foundation, Core Data, or any other Apple framework. Trust that these frameworks do what they need to do (if they don’t, file a bug). You should be testing your code, not Apple’s.

2. Unit Testing Requires Maintaining Two Apps

Yea, kind of. If you look at it like you have to maintain two separate apps, then you will certainly not want to test. But if you look at writing tests as writing the built-in assurance of maintaining against regressions you could inadvertently introduce in your code (I know YOU don’t do that, but I do, because I’m a human), then you might not mind so much. For me, I love the fact that I can quickly press Cmd + U to run my unit tests after making changes and immediately find a bug that I might have shipped, versus relying on my or my QA tester’s (or my own) abilities to happen to find it at runtime before shipping.

I reiterate the fact that you need not test absolutely everything, as that can also feel like you’re wasting time testing each and every minute detail that you’ve done a thousand times and know works. But what you do test will serve as a safety net for you in the long run. So start small and build up your confidence.

3. My Thoughts on Why You Should

Writing tests can help you build better APIs. Why? When you treat classes, structs, functions, etc., as small, testable segments that are easy to reason about, the code you write to pass your tests will be just as easily usable by your actual application as it is your tests. It can make code more reusable when you realize how decoupled your code can actually be.

Tests can help you realize what should belong to what class/struct/enum, and what could be passed in as a dependency. Dependency injection can really help you reduce the size of your code base as pieces can become more reusable by passing in an instance of something rather than needing to have separate functions/objects/etc just to accommodate working with something of a different type or some small difference. Isolate what changes, make it be passed in as a dependency, and now you can test and use it more easily.

Tests can serve as a great safety net, as mentioned before. I once had written a bunch of unit tests for testing view controllers (yes, could be overkill), but when refactoring storyboards and adding/removing outlets and actions, it proved to be an amazing safety net. In a matter of a few seconds I could quickly test that outlets were connected, actionable items contained the appropriate actions, and those actions performed properly or produced the proper results, versus having to navigate to a view controller 8 deep in my navigation stack and test each scenario manually.

Tests can give you great experience with structuring code that can help you in future jobs, projects, or environments. Not learning how to test your code is doing yourself a disservice. If you go to another arena, you’ll be thankful you’ve learned the foundations of testing. This knowledge can help teach you about the structure of testing should you ever go to a new platform, or when a project requires testing.

Summary

The points that these two hosts made were certainly valid, and are certainly valid concerns about why they do not prefer testing. But that’s just it, it’s a preference, it’s not a “thou shalt not test” thing. If you want to start, go for it. There are many resources available online to learn how to start. As mentioned, exercism.io is a great resource, my Concepts in Code podcast is another, and a favorite of mine is qualitycoding.org by Jon Reid. Best of luck to you, @b1ueskyz, and to any others who may be inspired by this post.