Article thumbnail

Understanding the integration tests

4m
frontend
testing
quality

Intro

Introduction to software integration testing in the React ecosystem with react-testing-library and jest.

Definitions and required theory

Before you read this article please make sure that you understand unit tests concepts - here you have Unit tests review.

An integration test is a type of software testing that focuses on evaluating the interactions and interdependencies between different components or modules of a software application. The primary goal of integration testing is to ensure that these individual parts of the software work together correctly as a whole, rather than just testing them in isolation.

Here are some key points to help you understand integration testing:

  • interaction points - communication of the modules, where it happens, how it happens,
  • scope - the part of the feature to check,
  • testing real-world scenarios - integration tests verify business cases, flows, whole or part of the program,
  • stubs and mocks - like in unit tests they may require stubs and mocks,
  • identification of issues - they should try to detect a real business/complete feature problems.

If you want to check the different cases and many unit, integration, and e2e tests - check the Dream stack for React dev repository.

To understand what stubs and mocks are, check the following articles: Understanding stubs in testing and Understanding mocks in testing.

Understanding the feature

Before you start writing integration tests you need to understand the whole or part of the feature that you want to cover. Without this is really hard to write bulletproof tests that will give you real value (will detect problems in your modules communication that may break your feature).

So, let's introduce our feature - the sign-in one.

Yea, again this repetitive concept but it's really great to show how integration tests work.

Loading

The demo of sign-in feature

Let's forget about implementation and focus on parts that are worthy to cover.

  1. Is the loader shown and the submit button is disabled during submission?
  2. Is an alert with an error displayed when credentials are wrong or another server error occurs?
  3. Is an alert with success displayed when the user is signed in?

The current implementation of sign-in

There is no need to deep dive into each module that we're using here - the most important part is that we are relying on other modules. In the example below, we're using Zustand stores, some actions, presentational components, and simple models.

If you want to understand the Zustand a little bit more, I recommend following 🥇 Comparing Redux with Zustand for state management in React article.

Loading

It's a typical scenario. The business state is outside of React ecosystem (implemented with Zustand), we have some dummy presentational components imported from design system library, and we're using custom hooks and other utils.

I skipped the implementation of other modules... Why I did that? Because in integration tests, they should be just black boxes for us. We shouldn't know "how they are implemented", but we need to focus on what are they offering to our consumer code - in this example SignInForm component.

Breaking the walls - let's write the first integration test

We know less or more how our feature works, what stuff is worthy of covering, and how our sign-in form is implemented. Now it's time to write well-defined integration tests for the SignInForm component.

Let's start from defining describe and it blocks.

Loading

We defined 3 cases to cover, but why do we add only 2 scenarios? It's because we'll check the disable of button during success flow and failure flow. They are different user journeys! So, separating this check into separate suites may not detect the issue for a particular journey - the button will be disabled only when submitting in success flow, but won't be disabled in failure one.

Now, we'll start from the integration test that verifies success flow and checks whether the button is disabled or not.

Loading

There are 2 problems with our test:

  1. we're not checking the alert message occurrence,
  2. under the hood the is a real HTTP request call.

Mocking the HTTP service layer

To understand what I'm talking about we need to review an action code of Zustand store.

Loading

To avoid HTTP request we need to mock the entire @system/blog-api module. It's not perfect, I know, and how we can improve that will be explained in the next lesson. Right now, let's just finish our test suites to understand the basics.

Loading

We completed our first integration test! Now it's time for the second journey - an error case.

Loading

We added weird statement in this test: jest.requireActual('@system/blog-api')['getError']. This perfectly shows, how bad is leaking into testing the internal implementation details of the module that our code under tests relies on.

This weirdo restores the original implementation for the getError function that is used by our signIn action to parse the failure scenario and retrieve the message from the response. Remember at the beginning of the file we mocked the entire @system/blog-api module - so the getError function is mocked too!

If you're interested in correct way right now, the one with MSW, just go under the following repository and check how it differs.

Making sure that tests are working

It's the best part of software testing. Try to break your implementation, remove some flags, functions, or change their implementation, to be sure that your tests spot problems with your code.

Loading

Dealing with broken implementation

This part is of course optional and if you have more practice, knowledge and experience, you'll mostly trust your tests.

Conclusions

Right now you understand integration tests theory and key points. You know how to write basic tests and how to localize paths to cover, but there is an important question: Are these tests good?

From the perspective of feature covering, they check all important aspects, but there are implementation details leak into our tests - a mock of the signIn service function, that's not good.

To improve our code we need MSW library, but this topic will be covered in the Using MSW library to remove implementation details from tests lesson.

Knowledge requires time, so before you will make things perfect, you need to fail and learn from mistakes.

I create content regularly!

I hope you found my post interesting. If so, maybe you'll take a peek at my LinkedIn, where I publish posts daily.

Comments

Add your honest opinion about this article and help us improve the content.

created: 10-04-2023
updated: 10-05-2023