End-to-end (E2E) testing helps with validating the most important flows in your application, such as user login requests. For front-end development, E2E tests help verify if the correct UI has been presented. For back-end development, E2E tests help verify if an important flow in your back-end services returns the expected output.
This post will introduce you to E2E testing and give you actionable tips. We’ll also explain how to write reusable tests so they can be used for creating E2E tests more easily. As you might know, E2E tests are often a major frustration for developers as they require a lot of effort to write, run, and maintain.
First, let’s take a look at where E2E testing is located in the software testing pyramid.
Place of E2E Tests in the Testing Pyramid
When looking at the testing pyramid, we find four main testing types to help us guarantee the quality of code:
- E2E tests help verify high-value paths in your application. In other words, they help verify user stories as they often represent flows in your application.
- Integration tests help verify the integration of multiple functions or components. Integration tests can tell you if your code works cohesively with other functions.
- Unit tests help verify the business logic of functions. Unit tests are the most basic form of testing, and they ensure the business logic is correct.
- Lastly, we can’t forget about static analysis or static testing. Static testing helps find typos or type errors.
According to this pyramid, most tests should be unit tests, with fewer integration tests and even fewer E2E tests. However, it’s still important to verify flows in your application. E2E tests are hard to maintain and require a lot of effort. Therefore, only verify the most important flows with E2E tests. Other testing types such as integration and unit testing should already give you sufficient trust in your codebase.
Different E2E Testing Strategies
Let me tell you that different strategies exist. We can choose between horizontal E2E testing and vertical E2E testing. Both strategies are worth exploring as they allow you to test different scenarios. Let’s learn what each strategy does.
Strategy #1: Vertical End-to-End Testing
This strategy focuses on testing all layers of the testing pyramid. It includes unit tests, integration tests, and UI tests. Your goal is to test a component on a granular level using unit tests, test how a component behaves when interacting with other components using integration tests, and lastly, verify the component’s behavior when users interact through the UI.
Most commonly, you want to use a continuous integration pipeline to automate these tests for every code commit or pull request.
Strategy #2: Horizontal End-to-End Testing
Horizontal E2E testing focuses on verifying a complete user flow. Here’s a sample flow for a webshop:
- Open product page
- Select product size
- Add product to shopping basket
- Go to checkout
- Complete address and payment information
- Complete payment
- Show shopping confirmation screen
Here, we want to verify each transaction of the user flow. Often, this type of testing verifies the interaction between different applications and verifies state changes for those applications.
Note that these types of tests are expensive to write. It requires quite some time to define user flows and write these tests. Therefore, try to focus on critical transaction paths for your application.
However, make sure to test these critical user flows for different conditions. This strategy allows you to get a complete image of the user flow’s robustness. These conditions include:
- Complex timing scenarios: For instance, a concert ticket is only available for you for 15 minutes.
- Different data input or missing data.
- Flow breakups: A user decides to leave the checkout page to view another product page. For example, we need to verify if the component saves the checkout state.
Next, let’s learn when to write E2E tests.
When to Write E2E Tests
You might not know that E2E tests are expensive tests to run in your continuous integration pipeline. They often require a lot of preparation, such as the creation of a database that mimics production data. In addition, they sometimes represent time-consuming flows in your application. Therefore, they can be slow and require more resources to be executed.
Use E2E tests to verify the most important flows in your application. Examples of high-value flows include:
- Login and logout
- Register a new user
- Add item to cart
- Change your password
- Any other important flows related to your service
However, do not forget to verify the business logic you write with unit and integration tests. The combination of both unit and integration tests should give you plenty of confidence about the quality of your code. E2E tests help you to increase this confidence for critical paths.
Here are some actionable tips for writing E2E tests.
Tips for Writing E2E Tests
1. Focus on Writing Reusable Tests
The first and most important tip is to write small, independent, reusable tests and components. This allows you to string together multiple small components to create complete end-to-end user flows more easily. Instead of writing a lot of custom components only to be used by E2E tests, focus on writing reusable components.
Writing small, independent components helps with the reuse of code and reduces error troubleshooting time. Also, it’s easier to update small components when functionality or user flows change.
2. Always Consider the Reason for Writing an E2E Test
When you want to cover a particular flow with an E2E test, consider if it’s worth covering. Only test high-value user flows with E2E tests. For example, you should not be testing if an error message is shown when a user inputs an incorrect email address. This is a far too simple use case that doesn’t fit an E2E test. This requirement can simply be tested with a unit test.
On the other hand, it’s worth testing if the login page redirects you to the right application page after successfully logging in. This is a valuable flow for your users to be able to use the application. In short, always consider the reason for writing an E2E test.
3. E2E Tests Shouldn’t Depend on Implementation Details
You don’t want to update E2E tests every time you change implementation details for a certain flow. Therefore, you want to write components that allow you to abstract implementation details. E2E testing becomes much easier to maintain, and more fun to write, when implementation details are hidden in components. In short, E2E tests should be independent of implementation details.
Next, let’s look at the pitfalls of E2E tests.
Pitfalls of E2E Testing
Here’s a list of common pitfalls associated with E2E testing:
- E2E tests should not try to test as much as possible in one go. Often, developers write huge E2E tests that verify every aspect of the user flow. Keep things simple and verify the most important aspects of your user flow. For example, for a login flow, you only need to verify if the user got redirected to the application page. This will make it easier for developers to maintain and update E2E tests.
- Whenever an E2E test fails, it can be difficult to find the exact cause of the failure. However, you can reduce the debugging efforts required by covering your business logic with unit and integration tests. Some tools also provide good root cause analysis including screenshots and console logs of failed test steps which can reduce the time to troubleshoot.
- E2E tests require a considerable amount of time in your continuous integration (CI) pipeline. They can block your CI pipeline, which slows down overall development. This means you might need to invest in your CI pipeline to have a few more pipelines for running tests.
- E2E tests are a continuous effort. Every time new functionality gets added or old functionality changes, it’s likely you’ll have to update your E2E tests. However, we can reduce this effort by writing reusable components that abstract implementation details. In addition, some tools use AI to help adapt the tests when code changes, reducing test maintenance.
Now, let’s take a brief look at the differences between system testing and E2E testing.
System Testing vs. E2E Testing – What’s the Difference?
So, what’s the difference between system testing and E2E testing? System testing focuses on validating both the functional as non-functional requirements. Often, it’s completed by an external team that treats the application as a black box. In other words, they don’t know anything about the application’s functioning except for the requirements it should meet.
However, this form of testing is often confused with E2E testing as system testing is a form of E2E testing. Though, system testing verifies much more than E2E testing. For example, system testing measures scalability, performance, or reliability. Whereas E2E testing focuses on the correct completion of user flows.
Lastly, let’s summarize the learnings of this article.
Conclusion on E2E
End-to-end tests are pretty powerful as they help with validating high-value user flows or user stories. They help guarantee the quality of your application.
However, writing and maintaining E2E tests can require significant time and effort. It’s possible to reduce this effort by writing small, reusable components that abstract implementation details. Some tools also help create more resilient tests by using AI or multiple attributes to identify each element. Therefore, you don’t have to update E2E tests every time implementation details change for high-value user flows. In addition, reusable components allow you to string those components together to create E2E testing flows easily.
E2E tests should cover the most important flows of your application. They test the flows your users and customers follow to get value from your application. If they can’t log in or add an item to a cart, they will either leave (if they can) or call support if they are forced to use your application. For example, you want to cover the login and logout flow, but are less interested in verifying an error message after inputting a bad email address.
In the end, E2E tests should be fun to write. They should not require a lot of time and effort to write and maintain. Keep E2E tests simple and focus on reusable components, and you’ll see great results!
This post was written by Michiel Mulders. Michiel is a passionate blockchain developer who loves writing technical content. Besides that, he loves learning about marketing, UX psychology, and entrepreneurship. When he’s not writing, he’s probably enjoying a Belgian beer!