If I set an alias in before
hook, it will be accessible only for first test in the current context.
Alias which is set in before
hook should be accessible for all tests in the current context.
This code produces error: cy.get() could not find a registered alias for: '@gaStub'.
for the second test.
describe('Google Analytics', () => {
before(() => {
cy.visit('http://localhost:8080', {
onBeforeLoad: (contentWindow) => {
Object.defineProperties(contentWindow, {
'ga': {
value: cy.stub().as('gaStub'),
writable: false
}
})
}
})
})
it('should perform logging on first page load', () => {
cy.get('@gaStub').should('have.been.called')
})
it('should report pageview', () => {
cy.get('@gaStub').should('have.been.called.with', 'send', 'pageview')
})
})
Can confirm, this is bug in 0.19.4
and upcoming 0.20.0
. Simpler reproducible example in Kitchen Sink App:
describe('Kitchen Sink Tests', function(){
before(function(){
cy.visit('/commands/querying')
cy.get('.nav').as('mainNav')
})
context('Aliases', function(){
it('first test', function(){
cy.get('@mainNav')
})
it('second test', function(){
cy.get('@mainNav')
})
})
})
I looked at this, and this is not a bug, this is the correct and intended behavior of hooks.
Code in a before
hook only runs once and once only. When it reaches the 2nd test: the aliases have been reset since Cypress clears state in between test runs. Maintaining state in between tests is an anti-pattern and can lead to unexpected results, as the state can be modified between each test.
I unfortunately do not have a better document to link to that explains this, as we need to improve our documentation concerning these "lifecycle events".
Regardless of our strong opinions on not maintaining state in between tests, there is a proposal within Cypress to expose configuration to allow users to disable some of these "lifecycle events", For example, you would be able to say: clearAliases: false
for the lifecycle of your tests.
For now, all I can suggest is moving the cy.visit()
command to be within a beforeEach
hook. The problem with this is that it often slows down the test suite waiting for the cy.visit()
to resolve. There are some ways that you could make this process faster:
Do not load any unnecessary third party scripts when in tests. Since the cy.visit()
does not resolve until it's load
event is fired. You can conditionally check if your application is running within Cypress and choose what resources to load.
Use cy.request()
when possible to check the DOM content/state of your application. Sometimes a simple request of the html file can satisfy what you want to test. For example: cy.request('/admin').its('body').should('include', '<h1>Admin</h1>')
. This does not require waiting for all dependencies to load.
_First off, I love Cypress and am grateful for all the work the team has put into it._
The existing documentation seems to contradict what is described in the discussion here as the intentional clearing of state between tests. I agree 100% that tests should not share state. However, what the Cypress documentation currently says to explain shared contexts (in relation to aliases) is:
Mocha automatically shares contexts for us across all applicable hooks for each test. Additionally these aliases and properties are automatically cleaned up after each test.
Emphasis on across all applicable hooks for each test. A user could read that documentation and reasonably expect that within _any_ specific test she or he should have access to the "context" from the beforeEach
and the before
hooks associated with that test (and any parent blocks), even if the test is not the first in the block.
I understand why visiting a page and inspecting the DOM in a before
hook, aliasing DOM elements, and then expecting those aliases for be good in multiple tests would be ill advised... but what about selecting specific data from fixtures in a before
hook that sets the stage for all tests in the block?
@jennifer-shehane
I think, aliases from before hook should be available through all tests.
An example which I have: usually you are preparing your db in before hook as you want to add only one element used for the testing, then you want to create an alias of this element to use it in each test...but this is not possible right now.
However there is a workaround here: using backflips! Which is against your documentation: https://docs.cypress.io/guides/core-concepts/variables-and-aliases.html#Return-Values
Using backflips works; so seems you are not clearing the state of let variable between tests which is differenet than what you wrote here:
Cypress clears state in between test runs
Regarding below:
Regardless of our strong opinions on not maintaining state in between tests, there is a proposal within Cypress to expose configuration to allow users to disable some of these "lifecycle events", For example, you would be able to say: clearAliases: false for the lifecycle of your tests.
Is this already in some "in progress" state?
OK seems I have found a solution for that; you need to put your tests in separate contexts like so:
describe('Describe', function () {
before(function () {
addDbElementAndReturnItsKey().then(elementKey => {
cy.wrap(elementKey).as('elementKey')
})
})
context('Context1', function () {
it('Test1Context1', function () {
// You have access to elementKey by this.elementKey
})
})
context('Context2', function () {
it('Test1Context2', function () {
// You have access to elementKey by this.elementKey
})
})
})
I am not sure if this is Mocha and Cypress specific or a bug:) but it works!
@kapalkat You can follow the LifeCycle Events issue here: https://github.com/cypress-io/cypress/issues/686
I can see that this issue is closed, but I still would like to comment.
I am a QA engineer writing test automation for 7 years now and I am new to Cypress. Behaviour, such as sharing context from before hooks with all subsequent tests within a block, is almost standard for so many other tools that the current behaviour is really confusing. I see that Cypress team is taking their own way, but there are many people who would still need to use other tools beside it. Always keeping in mind that Cypress is "different" is really exhausting and I just wish it would work the way people would expect more often than not. It is a tool and it should help, it shouldn't protect me from using anti-patterns or whatever.
The behaviour implemented by Cypress of clearing state between tests forces repetition of steps.
Imagine a test composed of 20 ordered steps.
NB: This is the overwhelming majority of use cases in most applications because the user has to have done something, in order for the user to do something else.
With this behaviour, if one wants to verify the state of the application after each step of the test, ALL previous step need to be repeated.
If state was not being cleared after each test, then no repetition would be needed.
The way in which mocha (on which Cypress is based) executes describe
, it
, before
, beforeEach
, afterEach
and after
blocks makes it clear that whatever pre-conditions were setup in the before
block should persist for the entirety of the describe
block. Whatever post-conditions are expected to be cleaned up after the describe
block should be present in the after
block. Similarly, beforeEach
and afterEach
allow pre and post conditions to be set for all it
blocks within that particular describe
block.
It is also significant that all describe
and it
blocks run in sequence, and so, the ability to encode an ordered set of tests is trivially available in mocha.
If there are pre/post-conditions to tests that are truly independent, then the user can either use beforeEach
/afterEach
or use a separate describe
block.
The way Cypress maintains state currently, is counter intuitive, and does not conform to what vanilla mocha implements.
Anything done in a before
block should remain for the entirety of the describe
block.
Please follow the LifeCycle Events issue and 馃憤 support for that issue there. That proposes more control of when and how state is cleared in between tests. https://github.com/cypress-io/cypress/issues/686
We'll be locking conversation on this issue as it is closed and not being followed. Please comment in #686
Most helpful comment
The behaviour implemented by Cypress of clearing state between tests forces repetition of steps.
Imagine a test composed of 20 ordered steps.
NB: This is the overwhelming majority of use cases in most applications because the user has to have done something, in order for the user to do something else.
With this behaviour, if one wants to verify the state of the application after each step of the test, ALL previous step need to be repeated.
If state was not being cleared after each test, then no repetition would be needed.
The way in which mocha (on which Cypress is based) executes
describe
,it
,before
,beforeEach
,afterEach
andafter
blocks makes it clear that whatever pre-conditions were setup in thebefore
block should persist for the entirety of thedescribe
block. Whatever post-conditions are expected to be cleaned up after thedescribe
block should be present in theafter
block. Similarly,beforeEach
andafterEach
allow pre and post conditions to be set for allit
blocks within that particulardescribe
block.It is also significant that all
describe
andit
blocks run in sequence, and so, the ability to encode an ordered set of tests is trivially available in mocha.If there are pre/post-conditions to tests that are truly independent, then the user can either use
beforeEach
/afterEach
or use a separatedescribe
block.The way Cypress maintains state currently, is counter intuitive, and does not conform to what vanilla mocha implements.
Anything done in a
before
block should remain for the entirety of thedescribe
block.