Integration and Unit Tests: How to Choose the Right One?

Avatar photo

By George

8 min read
Bookmark this post

If you are making your first steps in code testing, I want to start by congratulating you on walking that path! 🥳 This is a crucial decision and a great addition to your skill set. Before writing some tests, it’s vital to grasp the difference between two fundamental testing methods regarding integration and unit tests. In this post, we’ll dive into these testing types and explore their differences.

Testing scenario for our comparison

To understand the difference between integration and unit tests, let’s use an example of a function that calculates a user’s daily budget. We’ll name our function determineUserBudget and have the following flow:

  • Takes a userId parameter to perform a search for a user.
  • Uses the provided  userId to search for the corresponding user in the Users table of our database.
  • If we call the function without passing a userId, it will throw an Error to indicate the missing parameter.
  • Furthermore, if no user is found, it will also throw an Error to signify the absence of a matching user.
  • However, if a user is found, it will use the user’s info and call our Budget service.
  • Subsequently, it will perform budget calculations based on the retrieved data.
  • Lastly, the function will return the budget as its final output.

Let’s see what was just described through the following diagram so that it is easier to understand the function flow:

To differentiate between the internal workings of the function and the external connections that need to be made, I am using a dotted border.

Now that we have our testing scenario, let’s see what’s the difference between these two types of testing, and how we would test our function in each method.

(We won’t be writing any code in this post).

What is a unit test?

When writing a unit test we want to isolate the part of the code we are testing from the rest of the system.

This Is The Main Characteristic Of Unit Tests, Isolation!

Consider it as if you are focusing all your energy only on what’s happening inside a function without any regard for the function’s calls/interactions with the database, services, or any other invoked functions.

In our scenario, to unit test determineUserBudget , we’ll ignore the call to our database and the call to the money service. Our focus will solely be on testing if all potential function flows (see green arrows) are followed as expected.

So, for the unit testing methodology, here are some testing scenarios we could write regardless of the testing suite, to ensure comprehensive coverage of your function:

  • it should throw an error if no userId is passed.
  • it should make a call to the database if the correct userId is passed.
  • it should throw an error if no user is found.
  • it should contact the Money service if a user is found.
  • it should return the calculated user’s budget

However, what about the two calls in the database and service? How can we isolate our function from these calls?

When writing unit tests we need to substitute the external calls with mock functions so that we can simulate the call without actually making it.

A mock function is a function that replaces the behavior of an actual function during the testing.

To create an effective mock function, it’s essential to understand the response type and structure of the function being replaced. Understanding the response type and structure allows us to mimic the behavior of the original function and ensure that the mocked function provides the expected data during our testing.

After replacing the external calls with mocked ones we can safely focus on testing the function without any interactions with the external environment. This isolation allows us to concentrate on the internal workings of the function and successfully unit test it.

What is an integration test?

In contrast with unit tests, the keyword on integration tests is not isolation but interaction. Through integration tests, we are testing how different parts of our code interact with each other.

The Key Word On Integration Tests Is Interaction!

In our testing scenario, we will test much more! We will test our function’s results, including the interaction with the database and the correctness of the budget returned. The difference with the unit test is that we will pass “real” data and receive back “real” data to test.

In the integration testing methodology, the following testing scenarios could be some examples you could use based on our use case:

  • it should return X budget when a user is approved.
  • it should return Y budget when user is new.
  • it should follow Z budget calculation methodology when user is pending confirmation.
  • it should calculate the budget correctly for a user with a positive income and no expenses.

As you can see, the nature of our tests changed; we shifted our focus from testing the internal flow, to verifying our function produces the desired results, data, and budget.

Writing integration tests can be challenging because they require proper data preparation before executing the actual test. It’s quite common to spend more time during the initial steps to understand the required data and set up the necessary test environment before successfully executing the test.

Trusting external package providers

See that I didn’t mention testing the connection with the service? Well even if these are integration tests, they still don’t test connections with external services/providers.

Through integration tests, we are testing the way our functions interact with each other, not how they interact with external service functions. These functions are expected to have been tested by the service provider.

So, if for example you like using lodash and have used isEmpty in your function, you should not write unit tests about lodash isEmpty. It should be taken for granted that isEmpty is a black box for you that has been thoroughly tested by lodash’s developers.

Caution when using real data

It’s important to note that when I mention using real data, I am not referring to the same data used on the production site. Using production data in testing can lead to serious issues, like GDPR compliance and the leak of sensitive data.

Instead, by “real” data, I mean realistic/similar data, that the testing suite will use based on the testing environment. For example, when testing locally, the test would use data from the local database. When testing on a staging environment, the test would use data from the staging database.

A staging environment is an environment we use for testing features and bug fixes before deploying them on production. A safe simulation of a site or software used in production.

You should anticipate that these databases will be populated with data that is structured similarly to your production database, but not identical.

Which testing method to choose?

While there isn’t a definitive answer to this question if I had to answer that, I’d say, “It depends.” And yes I know you probably don’t like that answer. In that case, if I had to choose one answer, I would say that you always need to choose both types.

Unit tests are awesome for checking function flows, but integration tests are the best so that you may have a good night’s sleep! 😴 What I usually do is follow an approach that combines both. For complex functions, I write at least one integration test and multiple unit tests for each individual function the complex function relies on.

I consider writing tests a form of art, and while it may take time to master, incorporating it into your workflow will bring immense value. Once it becomes an integral part of your everyday workflow, you’ll experience a remarkable mind-shift that will not only enhance your testing skills but will also change the way you implement a feature. You’ll begin wearing two hats, the developer and the tester. By doing so, you’ll be able to proactively think about edge cases, error conditions, and much more while implementing your features!

I can assure you it’s a mind-blowing and life-changing process. So what are you waiting for? Go for it!!

DigitalOcean Referral Badge
guest
0 Comments
Inline Feedbacks
View all comments

Continue reading

Tools

Remote Web Developer Jobs: Discover The Best Sites Now

Tools

A Guide to Better Pull Requests: Prerequisite Changes and Splitting

Tools

How to Deploy A Next.js App with Render.com (2024)

Tools

What is Scrum? 101 Guide For New Developers

Tools

Why Did Your New Next.js Deployment Fail on Render.com?

Tools

How To Create Better Pull Requests: 3 Proven Tips

Subscribe to our newsletter

Dive into the Fun Side of Tech! Posts, News, and More Delivered to Your Inbox!

Intuit Mailchimp