79

Basically, I want to login once before all my tests in all files are executed.

Should I call my login command in each test file using the before hook or is there any way to do it once before all tests?

BinaryButterfly
  • 18,137
  • 13
  • 50
  • 91
Florian F
  • 8,822
  • 4
  • 37
  • 50
  • I've read about the supportFile but I'm not sure it can handle asynchronous code like a network request. By handling I mean, make sure all the asynchronous tasks executed in this file are resolved before launching the tests. – Florian F Jan 08 '18 at 09:48
  • There is nothing different from the `supportFile` than any other spec file except that it is loaded before the other spec files. If you are looking to handle some asynchronous requests, you should be using [`cy.request()`](https://on.cypress.io/request) for this. – Jennifer Shehane Jan 08 '18 at 15:17

7 Answers7

88

Short answer: You can write your login command in a before hook within the supportFile (the file that is loaded automatically before your other spec files). This before hook will run before any of the code in your other test files.


Recommendations: That being said, this approach leaves little flexibility for variation in your individual test files that you may want in the future like:

  • What if you want to seed the database differently for one test?
  • What if you want to log in as a different user with different permissions?
  • What if you need to do something in onBeforeLoad once?

I would recommend just having the login command in a before hook in each individual spec file.

I would also further recommend having your login command in a beforeEach hook to avoid sharing any state in between tests.

Jennifer Shehane
  • 6,645
  • 1
  • 30
  • 26
  • So the before hook is equivalent to `beforeAll` in other frameworks? Good to know, because doing say 'beforeEach(... cy.visit(myUrl)` is really tedious at runtime, when you're just testing that a bunch of static features exist on the page. – Richard Matsen Jan 10 '18 at 21:30
  • 10
    Yes, the `before` hook is equivalent to `beforeAll`. – Jennifer Shehane Jan 11 '18 at 22:25
  • 5
    Why would you force a login before each test? I agree in general with the decision of avoiding share state between tests, but I would very much make things like logging in an exception to that rule. Logging in is usually generally takes a second or more. Why would you add that overhead to each test? I think a better alternative to this would be to use [preserve once](https://docs.cypress.io/api/cypress-api/cookies.html#Preserve-Once) to speed things up. – Guido Tarsia Oct 11 '18 at 18:15
  • 1
    @erandros I can understand your concern. Personally I think it's best to be aware of login and state/session sharing, perhaps logging back in for each set of related tests. Another idea to consider is using cy.request() to POST to login to log a user in instead of using the UI to login in since it's faster (you can also grab or handle cookies during this POST login). Generally though I think Jennifer is correct to warn about sharing state between tests. Ideally the order of tests being run should NOT influence the tests outcome whatsoever. – RoboBear Jul 17 '19 at 23:05
  • @JenniferShehane, is there a possibility to run a command globally not in each test? I need to run a command 1 time for 10 tests not to repeat the command 10 time. Could you help? – Asking May 18 '22 at 09:24
12
describe('Hooks', function() {
    before(function() {
        // runs once before all tests in the block
    })
})

https://docs.cypress.io/guides/core-concepts/writing-and-organizing-tests.html#Hooks

Hossam Mourad
  • 4,369
  • 4
  • 26
  • 22
9

Now in Cypress v12 the appropriate way to do this is with the Before Run API.

cypress.config.js

const { defineConfig } = require('cypress')

module.exports = defineConfig({
  // setupNodeEvents can be defined in either
  // the e2e or component configuration
  e2e: {
    setupNodeEvents(on, config) {
      on('before:run', (details) => {
        /* code that needs to run before all specs */
      })
    },
    experimentalInteractiveRunEvents: true,   // use for cypress open mode
  },
})
Aureuo
  • 202
  • 1
  • 7
7

I would login before EACH test because there could be things that happen in a previous test that affects the results of the current test. With a fresh login, you're starting with a clean state every time. If you want to test a "chain" of unrelated actions (action A THEN action B), then write that as a separate test, but have basic functionality in individual tests.

describe('/page'), () => {
  beforeEach(() => {
    cy.login()        // custom command that handles login w/o UI
    cy.visit('/page') // go to the page you are testing
  })

  // tests
})

You should include a beforeEach block in every test file. This block should login and navigate to the page in question.

metodribic
  • 1,561
  • 17
  • 27
Ethan Dowler
  • 127
  • 2
  • 4
  • 2
    "...there could be things that happen in a previous test that affect the result of the current test..." -- not if you're writing good tests. Tests should not rely on any state changes from separate tests, nor should they make changes that are permanent or carry over to other tests. Mock what you need to! – GrayedFox Sep 13 '18 at 17:33
  • 3
    I think @GrayedFox didn't get the point you were trying to explain. Basically, he has expressed the same idea you were trying to express. +1 to compensate the unfair downvote someone gave you. –  Nov 26 '20 at 13:14
2

cypress/support/e2e.ts

before(() => {
  // do stuff
})

Use before to run 1-time before every test or use beforeEach. to run before each individual test.

thisismydesign
  • 21,553
  • 9
  • 123
  • 126
1

I would wrap the function to execute in a before block, as others already suggested. Now, looking at the docs, I would make that happen in the cypress/support/index.js file. supportFile cypress docs section

Armando Guarino
  • 1,120
  • 1
  • 5
  • 16
0

Here is how I was able to work around the limitations of Cypress to get async code running in a beforeAll manner, in which it executes one time before all of the tests run.

  1. Create a new file named _beforeAll.spec.js in /e2e with a before block and at least one test (it can be as simple as logging something):
describe("_beforeAll", () => {
  describe("cypress setup", () => {
    before(() => {
      cy.seedApiDatabase();
    });
    
    // Note: It is important to have at least one test to trigger the before block
    it("seeds the database", () => {
      cy.log("Database seeded successfully.");
    });
  });
});

  1. Edit your cypress config (cypress.config.js/cypress.json) and add the _beforeAll.spec.js to the specPattern (See Cypress docs on E2E configuration for more info):
module.exports = defineConfig({
  ...,
  e2e: {
    ...,
    specPattern: [
      "./cypress/e2e/_beforeAll.spec.js",
      "./cypress/e2e/**/*.{feature,spec.js}",
    ],
  },
});

Anything added to the before block within _beforeAll.spec.js will be executed only one time before all of the tests run.

Note: This example requires a custom command "seedApiDatabase" to be added using Cypress.Commands.add("seedApiDatabase", () => {...}); within /support/commands.js to work properly.

Mike T
  • 513
  • 5
  • 14