Cypress Workshop Part 3: Test structure and test execution
Tags: qa, tutorial, automation, testing
Published: 2023-05-13 16:44:31

## LESSON 3: Test structure and test execution

For the purpose of this lesson, we will not go into the explanation of all project folders and files that can be used in Cypress for writing tests, but rather we will focus only on the main test file structure. That being said, main constructs which we majorly use in Cypress test development are: 

**describe():** It is is simply a way to group our tests. It takes two arguments, the first is the name of the test group, and the second is a callback function. (note: **context()** is is just an alias for **describe()**).

**it()**: We use it for an individual test case. It takes two arguments, a string explaining what the test should do, and a callback function which contains our actual test.

**before()**:  It runs once before all tests in the block.

**after()**: It runs once after all tests in the block.

**beforeEach()**: It runs before each test in the block.

**afterEach()**: It runs after each test in the block.

**.only()**: To run a specified suite or test, append "_.only_" to the function.

**.skip()**: To skip a specified suite or test, append "_.skip()_" to the function. (you can also put “**x**” in front of **it** block to skip it)

[Read more about test structure](https://docs.cypress.io/guides/core-concepts/writing-and-organizing-tests#What-you-ll-learn)

So let’s put this into action. 

1: Create new folder under `e2e` folder, call it for example `intro`, and create new test file under that folder, and call it for example `testStructure.cy.js`. Write the following code in the test file: 

```javascript
/// <reference types="Cypress" />

describe('Context: My First Test', () => {
  before(() => {
    // runs once before all tests in the block (before all "it" blocks)
  });

  beforeEach('Clear Cookies', () => {
    // runs before each test in the block (before each "it" blocks)
    cy.clearCookies();
  });

  after('Log something after all test runs', () => {
    // runs once after all tests in the block (after all "it" blocks)
    cy.log('we completed this test run!');
  });

  afterEach(() => {
    // runs after each test in the block (after each "it" blocks)
  });

  it('Test 1', () => {
    cy.visit('/automation-practice-form');
    expect(true).to.equal(true);
  });

  it('Test 2', () => {
    expect(true).to.equal(true);
  });

  xit('Test 3', () => {
    expect(true).to.equal(true);
  });

  it.skip('Test 4', () => {
    expect(true).to.equal(true);
  });

  it.only('Test 5', () => {
    expect(true).to.equal(true);
  });
});

```


![Test Structure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/awe32roooasgi9lc4g1f.png)

**Code explanation:**

As you can see, in this test file we have one _describe_ block, one _before_ block, one _beforeEach_, one _after_ block, one _afterEach_ and multiple _it_ blocks. It is possible to write test cases in different order and also do nesting one block inside of other, for example multiple _describe_ blocks under other _describe_ block, multiple _it_ blocks under other _it_ block etc, but for the sake of maintaining test file as simple as possible and as readable as possible always try to find one context of the test case - have one _describe_ block, and have multiple _it_ blocks for your sub-scenarios. 

If you need Cypress hooks (_before_, _after_, _beforeEach_, _afterEach_) you and also use them and try to use one or group them in meaningful way for your test case. These are not mandatory but they are useful and it will be explained later why. 

In this example, we have: 

- empty _before_ hook (empty hook will not run, and try NOT to have them in your code),

- _beforeEach_ hook that will clear cookies of our app

- _after_ hook that will log: 'we completed this test run!'

- _afterEach_ hook that is empty

- Test 1: that will visit one page of our app and assert that true is true

- Test 2: one assertion that true is true

- Test 3: one assertion that true is true (marked as skipped) 

- Test 4: one assertion that true is true (marked as skipped) 

- Test 5: one assertion that true is true (marked as the only test that will run) 

Giving all this, can you try to guess what will be the output of this test? 

If you said that execution order will be: 

1. _beforeEach_: `cy.clearCookies();`

2. Test 5: `expect(true).to.equal(true);`

3. _after_: `cy.log('we completed this test run!');`

you are correct! So, let’s prove it. You can run this test in several different ways: 

- look at `package.json` file and execute in VS terminal command: 

`npm run cypress-cli`

This will open Cypress client app and over there you can choose Chrome and test that you want to run. Refer to LESSON 1 for Cypress client explanation.

- you can also search for Cypress client app on your computer and start it yourself and you will have the same result as previous way. 

- look at `package.json` file and execute in VS terminal command: 

`npm run cypress-headed
`
This command will run your test WITH GUI mode, meaning in the background, cypress will run the test in headed Chrome browser. 

- look at `package.json` file and execute in VS terminal command: 

`npm run cypress-headless
`
This command will run your test WITHOUT GUI mode, meaning cypress will run the test in headless Chrome browser. 

Everyone has their own preference and reasons why they run tests one way or another while they are developing, but the easiest one is just to open cypress client and run test manually during development, so that you can easily debug it and manipulate it. So, let’s do it that way now, open cypress cli app and run the test by clicking on it. Which result did you get? 


![Test run results](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rilkjintbfk37sq3151i.png)

If you got this, then your test is running correctly. 

2: So, let’s play a little bit, and edit our test. Remove `.only` mark from your 5th test, and run the test again.


![Second test run](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p7z6giyt2szndu1xwdjo.png)

If you got something like this on the picture above, then you run the test correctly. You can see that once we removed `.only` mark from the 5th test, other tests were taken into consideration. You can see that test 3 and 4 are skipped because we marked it as skipped. Now, let’s try to enable them as well. Remove `.skip` and `x` in front of 3rd and 4th test and now all your tests should be executed in the order that you wrote them. 

```javascript
/// <reference types="Cypress" />

describe('Context: My First Test', () => {
  before(() => {
    // runs once before all tests in the block (before all "it" blocks)
  });

  beforeEach('Clear Cookies', () => {
    // runs before each test in the block (before each "it" blocks)
    cy.clearCookies();
  });

  after('Log something after all test runs', () => {
    // runs once after all tests in the block (after all "it" blocks)
    cy.log('we completed this test run!');
  });

  afterEach(() => {
    // runs after each test in the block (after each "it" blocks)
  });

  it('Test 1', () => {
    cy.visit('/automation-practice-form');
    expect(true).to.equal(true);
  });

  it('Test 2', () => {
    expect(true).to.equal(true);
  });

  it('Test 3', () => {
    expect(true).to.equal(true);
  });

  it('Test 4', () => {
    expect(true).to.equal(true);
  });

  it('Test 5', () => {
    expect(true).to.equal(true);
  });
});

```

![Third test run results](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zl0mxt5p6jtt448rc6wq.png)

You can open each test and see what happened during its execution and debug if needed. 


![Test debug](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1lpj90un9q2mntnl1ryz.png)

Now, let’s try to fail one test intentionally. Choose for example second test and assert that `true` is equal to `false`. And you should get something like this: 

```javascript
  it('Test 2', () => {
    expect(true).to.equal(false);
  });
```

![Failed test](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/88rvfi1q9m0y22zzn19l.png)

You can see that second test has failed, Cypress tried to run it 4 times (as per our configuration, see - LESSON 2, the end). Now if you return it to the previous state, `true === true`, it will pass again. 

**So, how does this apply to a real life test scenario?**

Let’s say you need to write the test to prove that email field of a login form works correctly. For this test you will usually have to test some form of validation. 

Test context: Check email input field validation in the login form (this will be your `describe` block)

Test 1: Check if user is able to login with correct email format (first `it` block)

Test 2: Check if user is not able to login if email field is empty (second `it` block)

Test 3: Check if user is not able to login if email is incomplete (third `it` block)

… and so on. 

For this example, it would be good to clear cookies if your app is storing some, in order for test data not to interfere with other tests. Cypress should automatically clear cookies after each test, but depending on your test subject you might want to clear them yourself. Good practice is to clear any test state BEFORE each test. Not after. Read about this anti-pattern here: [Cypress hooks anti-pattern](https://docs.cypress.io/guides/references/best-practices#Using-after-or-afterEach-hooks)

Another example, let’s say you need to write test for some form inside your application, but you need to login first in order to do that. Since login is not the subject of that test, it is a preparation step, you should put that login part into `before` or `beforeEach` hooks. There is a newly introduced feature where you can use cypress session to set up test data: [Session](https://docs.cypress.io/api/commands/session).

## :pencil2: HOMEWORK

Play with test structure and try to guess execution order. 

Don’t forget to push everything you did today on Github :wink: Remember git commands? :wink:

`git add .
`
`git commit -am "add: first test"
`
`git push
`

<img width="100%" style="width:100%" src="https://media.tenor.com/dUiTmHdcDZQAAAAC/self-five.gif">

CONGRATS ON WRITING YOUR FIRST TEST, AND SEE YOU IN LESSON 4!

[Completed code for this lesson](https://github.com/ViktorijaFilipov/cypress-workshop/tree/main/LESSON_3)

If you have learned something new, feel free to support my work by buying me a coffee :coffee:

[![Buy Me A Coffee][2]][1]

[1]: https://www.buymeacoffee.com/viktorija3141
[2]: https://cdn.buymeacoffee.com/buttons/default-black.png