Unit tests or end-to-end tests in web apps? How to decide
End-to-end tests are a big part of modern software QA. I have previously written an article about the importance of software QA. While I am a big fan of e2e tests, there are some drawbacks to consider:
- The tests can be flaky due to numerous reasons (e.g. network), meaning you may get false negatives which you need to verify.
- It takes longer to run the e2e tests compared to unit tests.
- A web browser is required to run the e2e tests which takes up more resources.
In this article, I want to give an overview what you should actually test in end-to-end tests. Your mileage may vary but this article shall highlight (and discuss) how you can test a particular feature or component: either in a unit test or an e2e test.
For e2e code examples, I will use Protractor which is a JavaScript end-to-end test framework for (Angular) web applications.
Clicking elements
There are hardly any web pages without any interactive elements you can click. While simple click events can be tested in unit tests, you should write an e2e test if the click handling is more complex (e.g. because it affects things which are out of the scope of the component you are testing).
$('.submit-button').click();
Pressing keys
Whether you are performing keyboard shortcuts or filling out forms: in many web pages, you need to press keys on your keyboard in order to achieve something. Similar to click events, you should test keyboard actions in e2e tests when the event handling is more complex. Otherwise, checking this in a unit test can be enough.
$('.username').sendKeys('john.doe');
Get the number of elements
Many web pages include some kind of list. Often, you want to check that a list is not empty and has an exact (or at least) number of list items. However, you should use this sparingly. Instead of checking the number of elements, you should rather check whether the elements are equal to what you expect them to be. After all, the items could have been created through previous tests or they might display wrong data.
$$('.items li').count();
Navigation & routing
In single-page applications, you are routing without reloading the whole web page. You should check whether the URL is correct after you navigated to a certain part of your web page. You should at least once verify that routing through the UI works. Then, you can route directly via the URL in the following tests if that speeds up your testing (e.g. because the click path is too long).
$('a#bing-link').click();
browser.get('https://www.bing.com');
expect(await browser.getCurrentUrl()).toBe('https://www.bing.com');
Hovering elements
Often, hovering an element a tooltip will be shown or the styling of an element slightly changes. Similar to other user interaction events, you should consider a unit test if the event handling is rather simple.
browser.actions().mouseMove($('#go-top-button')).perform();
Checking whether elements are present / visible
In dynamic web apps, content often changes which means some initially displayed elements may disappear to make room for other content.
$('#submit-button').isPresent();
$('#submit-button').isEnabled();
Responsive testing
As any web page by today’s standards is expected to work on mobile and desktop devices, you can check the responsiveness of your web app in an e2e test. For example, you can check whether a particular element is hidden on a mobile screen but visible on a larger screen.
expect(await $('#menu-toggle').isPresent()).toBe(false);
browser.driver.manage().window().setSize(640, 360);
expect(await $('#menu-toggle').isPresent()).toBe(true);
Getting cookies / window storage
Most web pages set cookies or save some data locally in your browser which can be retrieved. As cookies and window storage implementation is different in various web browsers, it makes sense to check these cases in an e2e test.
browser.manage().getCookies();
browser.executeScript('return localStorage.getItem("' + item + '");');
Getting HTML attributes
While it is easy to get the value of a HTML attribute of an element in e2e tests, this is a task that can be checked in DOM unit tests more easily.
$('.user-table').getAttribute('class');
Scrolling (e.g. to an element)
Many web pages react to scroll events (e.g. Twitter fetches new tweets once you arrive at the bottom of the displayed tweets). As this is more of a UI topic, it can be easily tested in e2e tests.
browser.executeScript(`document.querySelector(${selector}).scrollIntoView()`);
Drag & Drop
Especially in single-page applications you will find features which rely on drag & drop. This is a typical task to be checked in an e2e test as it is a UI task.
browser.actions().
dragAndDrop($('#source'), $('#target')).
perform();
Conclusion
As you can see, it is often possible to test the same thing using either a unit test or an end-to-end test. There are some situations where either unit test or e2e test is more suitable for the given task. As a developer, you should evaluate whether a feature or a component should be tested in an unit test or an e2e test. It is also possible to combine the best of both worlds: check the more simple test cases in a unit test and check more complex logic in an e2e test.
Thanks for reading this article. Do you have other examples? Let me know in the comments.