Unit testing is one of those things that developers either love or hate, there is no middle ground with it. And it’s also one of those tasks that in many teams, gets delegated to the “new guy”, the Jr. developer or essentially anyone who’s not working on anything relevant enough.

This normally happens because unit testing is considered a trivial task, something that can’t be messed up by anyone. But guess what? There are ways to screw up your tests and here I’ll cover the top 3 mistakes you (and anyone else) can make during testing.

Tip: Use Bit to Test, build and publish small units in isolation from your project! No test or build configs required. Turns small modules and components into building blocks to develop and reuse in your projects, with zero overhead. Don’t test and publish entire libraries and don’t copy-paste code, when you can build faster with small reusable modules. Take a look.

Bit: Easily reuse and sync small modules across JS projects

1. Testing more than one thing at a time.

This one is a common mistake when you send new developers to test big chunks of code. Usually they find big functions and set out to try and test them out from top to bottom. And because they’re such big functions, they tend to use one or maybe two test cases for each one and end up with testing scenarios looking for unrelated things.

Let me ask you something: who’s messed that one up? Was it your test developer? Or whoever worked on the function with hundreds of lines inside it?

Writing unit tests means you need to focus on one particular unit of your code and if the function you’re testing doesn’t allow you to, modifying your tests is not the way to go. Instead, take the time to refactor the code, split it into more manageable units and split your test cases as well to make sure you’re able to test one thing on each one.

Take the following sample function for instance:

That function is perfectly fine, but if you’re trying to unit-test it, you’ll have a hard time trying to isolate parts of its behavior. It’s not easy to just test the validation or the transformation done to the variables. If instead, you were to refactor it and split it into simpler, smaller functions, such as:

And with that refactor, you’re now able to individually tests different parts of the previous behavior.

2. Relying on external services

Most of the time, the code we write is not meant to work in isolation, in fact, it is meant to work with external services. And the word “services” here is used lightly, because it implies anything from a query to Google Map’s API to saving a file in your local hard drive.

Essentially, your code will interact with something outside it’s execution environment and your unit test should not rely on it. Why? Simply because during test execution you’re not (or should not be) responsible for the status of these 3rd party services. In other words, why would your unit tests need to fail if Google suddenly decided to take their Maps API offline for a few hours?

If your tests rely on that interaction then you’re not only testing your code, but the consistency of these services, which should not be the focus of your efforts. These external services are products on their own, and should’ve been tested during their development cycle, why are you worrying about it now?

And if you’re thinking: “well, I’m still testing the code that connects with those services”, well, unless you’re manually coding those drivers, they are 3rd party libraries that should’ve been tested by their own developers as well.

So what I’m trying to say here is: focus on your code, if you’re interacting with a database, mock it up, if you’re sending a request to an API, worry about mocking the responses, you get the point.

3. Aiming for coverage

Finally, another classical mistake people make when writing unit tests is aiming for coverage. How many times have you been told: “Write tests until you’ve covered 90% of your code with them.”

Coverage is just one metric for your tests, just like Lines of Code is just one metric for the complexity of your functions, but I’m sure you’ve seen some nasty one-liners that are really hard to understand at first glance, haven’t you?

You should not see coverage as a measure of what’s been covered, instead, you should use this metric to understand what’s been left uncovered and understand if it needs to be.

If you’re looking for a metric to measure your success by, try to go with the accuracy of your tests. In other words, instead of making sure you’re covering all code paths, make sure you’re hitting every relevant path with all possible variations. What do I mean by this?

Imagine you have the totally real and useful function shown below:

You can easily reach 100% coverage by testing that div(1,1) returns 1 and that console.log is getting called. That’s fine, however, I’m sure you can see how irrelevant those tests would be the minute you pass a 0 as the second argument to the function.

If instead, you’d add several tests making sure everything works with varied values for both, a and b but left out any tests for the content of your if block, that would be just fine.

Remember, focus on functionality !

Conclusion

So there you have it, those are the top 3 mistakes I’ve seen, and of course, done myself while unit testing. Can you think of any other classic mistakes devs fall for while writing tests? Share them down in the comments!

Thanks for reading, see you on the next one!

Learn More