More tests with cypress.io!
In a previous article, I covered integrating and writing your first tests using the end-to-end testing platform, cypress.io. This week, let’s delve into writing tests for some React components.
I recently was challenged to build a React app that had a few requirements. They included:
- Use an API to fetch an object
- Create a
home
page that displays the latest image on load - Create a
search
page with an input form to search & render an image
Some of the additional elements that were required involved specific className
‘s for elements, which was a critical step in completing this challenge. I always hear the faint voices of my instructors, “READ THE F*CKING README.” Thanks to Tez & Hanaa for pushing me to improve my attention to detail!
Now normally, if I wasn’t under a time constraint, I’d follow TDD principles, but I need to be honest with myself, and YOU, I’m still learning and not proficient at writing tests while under the gun!
Adding Cypress to existing projects
One of the great things about using the cypress.io desktop client, you can drag and drop a project folder and et voila! Cypress is ready to go in your project. Alternatively you can use the command line by cd
’ing into your project folder and using the below commands to install Cypress locally as a dev dependency for your project.
npm install cypress --save-dev
The tests we’ll be writing cover the following:
- Our API responds and returns an object
- Test our
home
component for specific criteria - Test our
search
component for specific criteria - Test our
navbar
component for specified criteria and navigation ability - Test our search form functionality
API tests
This might be one of the most important tests we write for this challenge. It’s the piece that requests and returns the data we want to display. Let’s remember we’ll need our describe
or context
block, it
function and finally the assertion
or should
.
In our first API test, we’re wanting to make sure that we get a status code
of 200
and a status text
of OK
. It might look something like:
Once we run it we’ll know that our API is working and returning a positive status. This leads to our next portion of our API test - the returning of an object of data.
We can use the same spec file to chain on another test to check the data we are returning. Having also tested the API in my favorite API tool, Postman, I knew that a single object will be returned with 11 key / value pairs.
The test I’ve implemented below will verify the schema of the returned object:
Home page tests
Our home
page will need to display an image and also has three className
requirements as outlined in the graphic below:
Great, let’s write some cypress tests to check those requirements off the list.
Our first home.spec.js
test, will check that specific elements on load have the correct className
‘s. We first send the test to the correct URL, ask it to look for a specific dom
element, and it should expect to have the correct className
.
describe('Home page tests', () => { beforeEach(() => {
cy.visit('http://localhost:3006/')
})it('should have specific class names for elements', () => {
cy.get('.search')
.should('have.class', 'search')
cy.get('.latest')
.should('have.class', 'latest')
})
Now that we understand how to write tests to check for className
elements, let’s move on to the next home.spec.js
test for checking if my fetched image gets rendered. We again point it to the correct URL, it should get a specific dom
element with the className
latestComic
, and finally find the image and make sure it is visible.
Search page tests
Our search
page/component has similar tests to our home
page with regards to specific className
‘s.
Our implementation of those elements will look like this:
Form testing
An additional feature of our search page is the form. This form will will take a value and then display an image.
Our first set of tests should select a specific dom
element with a name of .searchInput
, type
s some text, check its presence, clear
it and finally check ensure there is no value.
context('Check search form functionality', () => {
beforeEach(() => {
cy.visit('http://localhost:3006/search')
})it('.clear() - clears an input or textarea element', () => {
cy.get('.searchInput').type('Clear this text')
.should('have.value', 'Clear this text')
.clear()
.should('have.value', '')
})
The next set of tests takes us to form submission by again selecting a specific dom
element and entering a value.
it('.submit() - submit a form and display an image', () => {
cy.get('.searchInput')
.type('303').should('have.value', '303')
We then chain on the selection of another dom
element, this time the button, with className
searchSubmit
.
cy.get('.searchSubmit').click()
And finally we check that the image is displayed, again selecting a specific dom
element and ensuring it contains an image.
cy.get('div[class="searchedComic"]').find("img")
.should('be.visible')
Our full form.spec.js
should look like:
Navigation tests
The final set of tests will check the functionality of our navigation bar as well as the routes I’ve set up in the application. Those routes are /
and /search
respectively. Let’s dive right into it!
Our navbar has two clickable elements, a latest
and search
hyperlink. To ensure these are clickable elements, and that they load to the corresponding routes, we first need to select the navbar, look for the particular elements and then click them. Our test will look like:
describe('Test Navbar functionality', () => {
beforeEach(() => {
cy.visit('http://localhost:3006/'), cy.get('.navbar-nav').contains('Latest').click()
// Cypress checks for a HTTP 200 response cy.get('.navbar-nav').contains('Search').click()
// Cypress checks for a HTTP 200 response
})
The next part of our tests will verify path names
it('cy.go() - go back or forward in the browser\'s history', () => { cy.go('forward')
cy.location('pathname').should('include', '/')// clicking back cy.go(-1)
cy.location('pathname').should('not.include', 'search')// clicking forward cy.go(1)
cy.location('pathname').should('include', 'search')
})
Our entire full navigation.spec.js
should look like:
Conclusion
With the basic functionalities I’ve outlined above, you’ll get a long way testing your app. These examples show the ease and simplicity of cypress testing. It bundles the popular Chai assertion library and allows for simple and powerful chaining. In addition, the cypress.io website provides excellent documentation if you can’t find the answer to your problems!