Adding integration testing to an existing codebase with Cypress
You have recently come into your inheritance. Surprise! It's a giant Wordpress codebase with years of development history and no automated testing of any kind. Congratulations.
This is an unfortunately common scenario for developers, and it can turn what should be routine maintenance into time-consuming and expensive games of whack-a-mole. Clients get a big bill for little visible progress, get frustrated with development shop, move to next dev shop, where the process then repeats itself. It's not pretty.
Breaking the cycle with integration (or end-to-end) tests
It isn't realistic in most situations to retroactively add unit tests to codebases with a lot of history, because it's very rare for code that is not written test-first to play nicely with unit tests. If we do try to implement testing at this level, we tend to find that they are extremely time-consuming to write, while also being brittle and undependable.
In this situation, we are going to get the most bang for our (and our client's) buck out of integration tests, or tests that interact with our website in the same way that a user would. To write these tests we are going to spin up a copy of Cypress, an open source testing framework that makes a lot of the boring decisions for us, and lets us get started testing quickly.
Setting up Cypress
Install
In your terminal, navigate to the root of your project and install Cypress.
npm install --save-dev cypress
Update package.json
Set up an npm script to start Cypress from command line with npm run cypress
.
{
...
"scripts": {
"cypress": "cypress open"
...
},
...
}
Configure Cypress
Cypress created a file called cypress.json
. Update it with the url of the application you are testing.
{
"baseUrl": "http://localhost:8080" # YOUR TEST ROOT URL
}
Optional: Configure IDE
If you are using an IDE like VS Code, include this config in the root of your project in jsconfig.json
to allow for autocompletion. Add this file to your .gitignore
.
# ./jsconfig.json
{
"exclude": ["./node_modules"],
"include": ["./node_modules/cypress", "cypress/**/*.js"]
}
Remove sample specs
Cypress generates some examples in ./cypress/integration
when you first start it up. We don't need them, so clear them out.
Write your first spec
Create a new file header_spec.js
in ./integrations
. We are going to test that the home page has a header, and that it contains a logo with a link to the home page, and an element with the text "Blog" that is a link to the path /blog
. If you have your own sample project running, feel free to test whatever links you want.
# ./integrations/header_spec.js
describe("Header", function() {
it("has navigation links", function() {
cy.visit("/");
cy.get("header")
.find("#logo a")
.should("have.attr", "href", "/")
cy.get("header")
.contains("Blog")
.should("have.attr", "href", "/blog")
});
});
Save this file and in your terminal run npm run cypress
. The Cypress application will open with an automatically updating list of all the spec files in your codebase, along with a handy button to 'Run all specs'. When you click it, this button pops open a test runner that shows you everything Cypress is doing and the results of your specs. From this point, whenever you update your spec, Cypress will automatically rerun the tests.
Write more specs
Identify mission-critical functionality and get it covered with some basic specs. Good candidates for early coverage are logins, sign ups, and contact forms. Covering essential business functions of your site will give you the peace of mind that changes made to your codebase are not breaking the things that are most important to your audience, and allow you to confidently add features or refactor existing ones.
Dig deeper
READ: Introduction to Cypress
CLONE: Tiny Cypress E2E test case
WATCH: Best Practices Assert(JS) Talk