“A good programmer is someone who always looks both ways before crossing a one-way street” – Doug Linder.
One of the most effective ways to “look both ways” as a programmer is to implement automated testing. This can come in the form of integration tests, end to end tests, or unit tests (there are other types but these are the most common).
Integration Testing involves testing of multiple combined modules. Examples of this would be a UI component made up of methods, styling and component code. Another example could be an endpoint. The main advantage of integration testing is that it tests how various modules interact. The individual modules might work fine individually during testing but the interaction between modules might cause bugs. Integration testing is closer to how your actual application works but is still narrow enough in scope that you can identify the root cause of bugs.
Last is end to end testing. E2E testing is exactly what it sounds like. It tests the entire app from front to back just like an actual user would. Because E2E casts such a broad net it has the potential to find a wider variety of bugs than more focused testing like unit and integration. This also comes with the disadvantage of not always knowing what caused the bug. The problem could be anywhere in your tech stack (UI, an endpoint or any modules in between). When investigating an E2E test it takes a bit more work to figure out what exactly caused the failure.
The differences between different types of testing can be a bit subjective. One person’s idea of a unit test might overlap with another person’s idea of an integration test. Now that you have an overview of the most common automated testing types I will give a detailed overview of a tool that has grown a lot in popularity recently.
One of Cypress’s coolest features is that it takes snapshots at each step and you can hover your mouse over each stage to see what happened. These snapshots are not just images. When you select a snapshot in your test history, the page itself reverts to its previous dom tree. This means you can interact with the page and more easily find out exactly what went wrong. In addition to snapshots, Cypress includes a video feature which optionally stores a video of your tests being carried out. This feature is especially useful when included in your CI/CD pipeline.
When testing a full stack app, you’re usually waiting for requests to finish and DOM elements to load before your test assertions can be made. Thankfully with Cypress you usually do not need to write any logic and wait for elements to show up. In most cases, Cypress will automatically wait for an element to show up. The most common example is the cy.get() command. If I use cy.get(‘#some-id’) cypress will wait for this element to show before performing an assertion or continuing in the code.
One of the areas I have been using Cypress the most is accessibility. Cypress does not include specific accessibility features natively. It does however support 3rd party plugins like cypress-axe which replicates the full axe feature set. This plugin makes tricky tests like color contrast significantly easier. Simpler tests like checking for an aria-label can be done with native Cypress commands.
One last feature of Cypress is called stubbing. Stubbing is essentially mocking requests and responses. As shown in the image below cy.route() takes in a set of parameters to shape your http response or request.
Stubbing can have a number of benefits. A mocked response can be much faster than actual communication with a server. It also alleviates the need for seeding your database and is useful for recreating server errors and special conditions that may otherwise be difficult to recreate. You can even choose the amount of server delay.
Overall, I’ve found Cypress to be an incredibly useful and easy to pick up tool for end to end testing. Most of my needs have been covered by just a few built-in commands and when I’ve needed more, the support for third party packages has been sufficient. That being said, Cypress does have its shortcomings. Occasionally, parts of the app do not load quickly enough and the tests fail due to insufficient default wait time. Cypress then times out while it waits for the element to show up. Another inconsistency I have run into while using Cypress is keyboard behavior. Simulated keypresses frequently don’t work and tabbing through elements can be challenging. Despite these faults, I would still recommend Cypress to anyone who needs end to end testing for their app.