DEV Community

adegiamb
adegiamb

Posted on

1

Code Coverage - Aim for 100% but not 100%

Testing your code is an essential part of software development. But many developers fall into the trap of thinking, "I need to cover 100% of my code."

This can lead to a couple of issues:

  1. Developers write tests to hit the 100% target, create poor tests, and do not focus on testing business logic.

  2. Spending extra time, reducing the velocity of sprints, and delaying projects.

  3. Not taking the time to ensure tests are easy to follow and maintain.

Developers should aim to achieve 100% code coverage of the application's business logic. The business/client cares about this and usually considers it the most critical part of the software.

When you cover all your business logic, it helps improve:

  1. Understanding the side effects of future changes

  2. You can refactor your code more efficiently and confidently; everything still works afterwards.

  3. Reduce whack a mole between changes and deployments.

  4. Business requirements were met.

First-class citizen

In many organizations, testing is treated as a second—or third-class citizen when high estimates or deadlines are approaching.

Some common phrasing we hear:

Phrase Reality
We will return to adding unit testing after the launch. There is a slight chance this will happen since the next priority takes over.
Only a short period of time between projects.
They are not necessary, so we will do everything manually. The number of manual tests is growing, and deployments are beginning to slow down. Not all tests are run between each release, causing a chance for issues to be introduced.
It's a small change; don't worry about it. Many minor changes were added, and now you have a Jenga problem. One future change causes things to come crashing down.

It's with good attention to try to get the feature or release out quickly, but that can cause team members to focus on the short term and not think about the long term.

We have to help educate our partners and give them analogies to see the importance:

Analogies Question Risk
You are doing a reno, and the plumber installs the sink and dishwasher. Would you let the plumber leave without testing if any leaks exist? The pipe leaks and causes water damage in your house.
You are shopping for a used car/motorcycle. Would you give the seller thousands of dollars without a test drive? Vehicle engines have issues or need tons of work before becoming street-legal.
The cable company comes in to hook up your internet/TV. Wouldn't you want to test if you can connect and browse the web before they leave? The internet doesn't work when trying to join a meeting.

When picking an analogy, make it simple and a topic for the audience.

Over the years, I have been asked if I can split my estimate between development and testing; I usually won't do that since it is set up for the abovementioned issues. I remind them that testing is a step that needs to be done before the change can be reviewed and deployed.

Unit tests

When testing, developers spend a lot of time creating unit tests to ensure the business requirements are met.

In many cases, this will not cover all the code paths. You should take the time to test both happy and sad paths.

Ignore any option

You often use a mocking library to mock your repository and third party and call other services. Usually, the mocking framework allows you to use any for a parameter to make testing a bit easier. Try to avoid using it since it introduces a blind spot in your tests and gives you a false impression of testing.

Bugs

When a bug is logged, try to use Test Driven Development and first confirm you can duplicate the issue. This way, you can prove you fixed and resolved the problem, and from now on, you know that the bug should never come back.

Integration Tests

Over the years, while reviewing technical assignments, I have seen developers write integration tests using an in-memory database instead of the actual one.

Yes, 95% of the time, this will be faster to run, but you are trading off speed vs accuracy. If you are not using the same database as your production environments, you will be missing the following:

  1. Is your migration script written correctly?

  2. Could you tell me how your queries will perform?

  3. If you need to use SQL vendor-specific features.

  4. ORM can generate different queries, causing unexpected behaviours in production

To increase your confidence, use the same database and possible version your code will use in your environment. Everything will work the same.

Usually, when we do unit tests, we mock the repository layer and test it during integration tests. If the repository layer has business logic, you should test the business logic (branches) to ensure everything works correctly.

Not only happy Path

In most cases, pods focus on the happy path only for integration tests, and this shouldn't be the case. Add the needed tests if a layer can only be tested through integration tests and contains business logic.

Speeding up tests

As we all know, integration tests can be slow sometimes. Running them all during a build can slow down deployments and frustrate developers.

At some previous companies, I saw that they could tag critical path integration tests and run them on every build. Then, they had a scheduled task that ran against the main branch twice daily, ran all their tests, generated reports, and tagged the service owners with any issue they found.

It wasn't perfect, as some issues occasionally occurred before the scheduled task ran.

Input and Golden files

I was introduced to Golden Files when I started working with golang and becoming my go-to way of testing.

A good article is Testing in Go Golden Files.

In the past (I guess a lot of us), we would assert the properties of an entity to see if everything was okay. Over time, things can drift:

  1. New properties are added and are not part of the assertion.

  2. If we are not validating everything, side effects from changes are not always seen.

With input and golden files, you get a clear picture:

  1. You get a version history since changes are committed to your version control system.

  2. You can do a DIFF comparison on the files and see the change(s).

However, there are some drawbacks:

  1. Date/time fields add minor issues; in most cases, you must scrub them to a fixed value.

  2. Sometimes, changes can cause a lot of golden files to be updated at once, making PR look larger than they are. Most tools allow you to filter on file types, making it easier to see the actual changes.

Code Coverage Metric

Code coverage metrics are not the best way to understand whether you cover every u-case or the quality, but they give you a good idea of what has been tested.

What is more critical is taking the time during code reviews to check and understand the tests' actions and ensure that all branches are validated.

General tips:

  1. Aim to get as close to 100% for business logic.

  2. Make testing 1st class citizens.

  3. Don't focus on the 100% but on the quality of your test(s)

  4. Make sure your golden files are pretty printed to make DIFF easier.

  5. Name your tests to be straightforward to search for.

  6. If testing is challenging, it hints at a code smell, and you should rethink your implementation.

Top comments (0)

Heroku

This site is powered by Heroku

Heroku was created by developers, for developers. Get started today and find out why Heroku has been the platform of choice for brands like DEV for over a decade.

Sign Up

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay