How to think about Software Testing if you’re a human being

Development at Hubba
Product Development at Hubba
5 min readJul 25, 2016

I remember the time JP Boodhoo made my head explode. I had walked into a week long dev bootcamp thinking I knew a lot about how to build software. Our first day was an intense 9 hour session, second day was 11.. and it went up from there. On Friday we were at it until 2 am. By the time we were done, I had witnessed JP use BDD to drive out a web app framework + ORM + build and deployment scripts from scratch way faster than I could — even if I weren’t writing any tests. I couldn’t just ignore all of this, so I had two choices: start learning how to do all that stuff, or find another line of work.

Yes, your tests can be readable to all humans… not just devs!

That was 2008 and I’m still working on it! Good tests aren’t easy to write. I still don’t know the best way to do it but I’ve written a lot of them, seen others write them, read the literature, and watched the videos. Here are the 6 most important things I’ve learned so far:

1. Keep it clean and your tests can be your documentation

A few lines of code per test is really great. 20 or 30 lines is decent. A 100 line setup function is non-trivial and will take a long time to read and understand. When you’re refactoring, understanding the original code is pretty important.

Test setup should be easy to read. Keep each test small enough to be able to understand what it does at a glance. Variable and test case names should be expressive. “sentMessage” is easier to ingest than “sentMsg”, or “message” or “m1”. As a spec name, it’s faster to interpret “When a Hubba product is made publicly discoverable” than prdCntrl Discoverable”. Translating that stuff in your head over and over again is effort that doesn’t have to happen.

If you take the test example (above) and publish an HTML report from it, the result is decently readable. Want to know what the app does when you add a product to a list? Don’t want to wade through code? Now you can check the latest build report, and you got it for free. New devs joining the team? Check out the specs. It’s documentation that won’t fall out of sync with your code.

You don’t always get great documentation out of this. It takes effort to make it a habit and to write clearly and consistently. It’s sometimes not worth it. If you plan on keeping your code in production for a long time, my guess is it will be. Writing your tests with descriptive language means refactoring and changing the way your app behaves becomes easier. It means others won’t have to guess at acronyms, or have to read all the code to understand what’s going on. It also means ongoing change is less expensive in developer time and dollars.

2. Lean into your testing framework

Resist the temptation to build a layer on top of, or change the way your framework or test runner work. It’s extra effort that you probably don’t need, and you’ll end up forcing new devs to learn your custom stuff.

Some frameworks come with an API for performing assertions. For example, Jasmine has “toHaveBeenCalledWith” and “objectContaining” assertions that will let you assert that a spy was called with an argument you specify. Using these assertions is usually easier and more expressive than comparing values manually.

And read the documentation! A bit of reading can save you time and frustration while writing your tests. Read about the philosophy and motivation behind your chosen framework. Why did they build it? Is it a cheap clone of an existing framework? What sets it apart? What aspects of testing did the authors feel are most important? What are the right and wrong ways to write tests? Do you agree with the things the authors are saying? This is important, because if the authors believe things that you don’t, you may be forced to build your application using methods you don’t agree with, and that can lead to heartache.

3. 100% coverage is hard. You can do it, but you don’t have to

You don’t always need every inch of your code covered. Some code will probably never be changed or even looked at. Even if it is, sometimes it’s just not that risky to change it. Life will go on if you don’t catch that one logging statement with your tests. Make sure that the more critical parts of your system are covered. The code that calculates payroll should be at 100% coverage.

The faster you can write a clean test, the easier it’ll be to get that coverage. If test setup is long and difficult, you may want to refactor your design to make it easier to do. It’s likely that you’ll be simplifying both your test and production code as a result.

The best way I’ve found to get high coverage so far has been to write my tests first.

4. Writing tests first has the side effect of highly covered code

Writing production code before tests feels like eating dessert before dinner. I know eating dessert first sounds way better. I’m not against it because I love dessert, and I like it when I do stuff that works well. So if leaving your tests to the end works, then of course you should keep doing it.

I know myself, though. If I wrote my production code first, laziness or boredom or interest in the next new thing would compel me to write fewer tests. I’d probably misjudge how much time I’d need to write them. When do I stop writing the production code? Knowing myself, I’d use up all my time making my code as good as I can and I wouldn’t leave enough time for tests. Writing my tests first means I don’t have to think about them after I’m done, and I’ll probably end up with 90%+ coverage.

5. Writing tests first helps you think about what you’re building

Ever start writing code only to end up in a vortex realising way too late that you wrote completely the wrong thing? Maybe just me. TDD or BDD doesn’t always prevent that from happening, but sometimes it does. When I’m building something new, I sometimes start by writing out a few test cases without any implementation. This is way faster than writing actual tests, which means I can write a bunch of them and think about whether they make sense. I can change them, delete them all, and start writing real tests, having at least quickly hashed out a naive implementation in my mind. Once in a while, I’ll realise I’ve missed something before I start writing real code, scrap the original idea, and save myself some time.

6. Don’t stress

Try out some of the TDD katas, or build something that’s interesting to you and has nothing to do with your day job. Start by writing tests for the most error prone parts of your app. Let the tests reduce your stress. If you don’t already have tests covering some older code that’s not very test friendly, this book can help.

It took me a long time to get used to writing all these tests and it’s still a pain in the ass once in a while. I know this means writing a lot of extra code, but I’d still rather have the tests. And I can keep learning to write them in better and easier ways — so I can get to dessert faster.

Eddie Torrejon
Senior Platform Engineer
@etorrejon

--

--

Development at Hubba
Product Development at Hubba

MEAN stack. AWS. Data-focused and asshole-free. This is what interests us @hubba; we hope it interests you. And we're hiring.