This site runs best with JavaScript enabled.

Testing lists of items

Filip Hric

April 06, 2020


TL;DR:

  • you can test your lists using .then() or .each()
  • Cypress retries can help you test changes in your app
  • you can pass a function to .should() command

You can try out all the examples in this blog by cloning my repo. The app is there too.

Hello everyone 👋 We are going to test a list of todo items today. In my job as QA in Slido, I test lists a lot. In this blog I’m sharing some of my tips on how to that.

We can start by testing a list with two items. In a situation where we test a couple of items, the testing flow can be pretty straightforward. In our first test, we use .get() to select our todo items and then .eq() to filter the item we want to work with. Code looks something like this:

1/// <reference types="cypress" />
2
3const todos = require('../fixtures/twoTodos')
4
5beforeEach( () => {
6 cy
7 .request('POST', '/todos/seed', todos)
8 cy
9 .visit('localhost:3000');
10});
11
12it('Checks texts of todo items', () => {
13 cy
14 .get('.todo')
15 .eq(0)
16 .should('contain.text', 'buy milk');
17 cy
18 .get('.todo')
19 .eq(1)
20 .should('contain.text', 'wash dishes');
21});

We can make this code more compact. Instead of selecting each element individually, we can select them both and make a single assertion using .then(). When .get() command finds multiple elements it returns an array. This means we can reference each item as we would in an array, using items[i] where i is the index number.

1it('Checks texts of todos items', () => {
2
3 cy
4 .get('.todo').then( items => {
5
6 expect(items[0]).to.contain.text('buy milk')
7 expect(items[1]).to.contain.text('wash dishes')
8
9 })
10
11});

Testing longer lists

While this approach is nice, we might get into a situation where want to test longer lists. With 10 or more items, our code might get repetitive. So instead, we can select our todo items, and then use .each() command. This command works very similarly to a array.forEach() function and it enables us to work with items that are yielded via .get() command.

1it('Checks texts of todos item', () => {
2
3 const todosTitles = ["buy milk", "wash dishes", "clean windows", "clean up bedroom", "wash clothes"]
4
5 cy
6 .get('.todo').each( (item, index) => {
7
8 cy
9 .wrap(item)
10 .should('contain.text', todosTitles[index])
11
12 })
13
14});

Checking position of a certain todo item

Now let’s say, we want to check an item being on a first position, but our test starts with a different state. Imagine this is a live collaborative todo list and that we want to test a scenario where another user changes the todo list.

Here we have a test, that should have an item with the text „wash dishes“ on a first position. Our test starts with the item on second position, and during the test, we delete the first item.

1it('Has first todo item with text "wash dishes"', () => {
2
3 cy
4 .get('.todo')
5 .eq(0)
6 .should('contain.text', 'wash dishes');
7
8});

But this test fails! The reason is that Cypress’ automatic retries don’t query the whole command chain, only the last command. In other words, our .eq(0) is retried, but our .get('.todo') command is not. This means we are stuck with 3 todo items even after we delete the first one. See how the little blue “number 3" does not change after we delete the first todo item. There is a great article about this in Cypress documentation. To solve this problem, we can add an assertion for the length after our .get('.todo') command, so that we first assert the correct number of todo items and then assert the text of the first one.

1it('Has first todo item with text "wash dishes"', () => {
2
3 cy
4 .get('.todo')
5 .should('have.length', 2)
6 .eq(0)
7 .should('contain.text', 'wash dishes');
8
9});

This solution is not really satisfying, is it? We may be facing a situation where the number of our items does not change, but only the order of our items changes. Because we can use drag and drop in our app 😎. In that case, we can use .should() command and pass a function into it. There are many cool examples in the documentation on this. The final code looks very similar to when we are using .then(). The main difference is, that .should() commands uses retries logic, but .then() not use retry.

1it('Has first todo item with text "wash dishes"', () => {
2
3 cy
4 .get('.todo').should( items => {
5
6 expect(items[0]).to.contain.text('wash dishes')
7 expect(items[1]).to.contain.text('buy milk')
8
9 })
10
11});

That’s it. Hope you enjoyed this.

Share article
Cypress test automation course
If you like my articles, you’re going to love my new course on Cypress.io. It’s called Cypress test automation for people in a hurry, and it is exactly what you’d expect. A compact, fast and straight-to-the-point course with lots of practical examples and challenges. Check it out!

More articles coming! Get them to your inbox ✉️



Filip Hric © 2020