- Example application
- Support file
- Before hooks
- Before hooks when running all specs
- BeforeEach hook
- Solution
Example application
In our example application we have two spec files and a support file.
1 | repo/ |
The spec files have two tests each.
1 | context('spec a', () => { |
1 | context('spec b', () => { |
Support file
The support file is initially empty. Let's add a single console.log
message.
1 | console.log('support file') |
The support file runs in the browser, right before the spec file runs. We can see the support file and the spec file downloaded by the test runner in the DevTools console (blue arrow).
The two scripts (support file and the spec file) are requested by the test runner via XHR calls and then evaluated.
The above download mechanism is just an implementation detail. In effect it means the test runner is evaluating scripts in this order:
1 | <script src="support/index.js"></script> |
We can see that both files were bundled using Cypress' built-in preprocessor.
Which means our tests are really running the following concatenated script:
1 | // support/index.js |
Before hooks
Let's place a hook into support file and a hook into the spec file.
1 | console.log('support file') |
1 | before(() => { |
The test spec-a.js
runs and the DevTools console prints the expected output
1 | support file |
This makes sense - the support file comes first, thus its hook is executed before the spec file's hook. Similarly, if we add a hook to spec-b.js
it will execute in the same order
1 | before(() => { |
DevTools console messages:
1 | support file |
Before hooks when running all specs
Great, but what happens when the user selects "Run all specs" button?
We see 4 tests finish in the Test Runner, and in the DevTools Network tab we can see the support file plus each spec file requested by the test runner.
The scripts are then evaluated one after another, which is equivalent to running the following test code
1 | console.log('support file') |
The console prints the messages we expect
1 | support file |
Good, no surprises here, but notice that all hooks executed before all tests. This is noticeable when adding a log message in each test.
If you assume that spec-b: before
hook runs AFTER tests from spec-a.js
but before tests in spec-b.js
, you might get a wrong result here.
BeforeEach hook
Let's switch from before
to beforeEach
hook.
1 | console.log('support file') |
1 | beforeEach(() => { |
1 | beforeEach(() => { |
When we run a single spec, the DevTools show the following messages.
Spec-b runs the same way by itself.
Now let's run all specs together. Just like before, this will be equivalent to executing this concatenated script.
1 | console.log('support file') |
Do you see the problem? Look at the printed console messages.
Every hook has run for every test. We probably did not mean for the beforeEach
hook from spec-b.js
to run before every test from spec-a.js
, right? But because they were all in the same script at the root level, all three hooks are executed for every test.
Solution
Never use "Run all specs" button. In fact, when you execute
cypress run
, Cypress never runs all specs together. Instead it executes "support file + spec-a", then it separately executes "support file + spec-b" scripts.Be wary of placing
before
orbeforeEach
hooks at the root level, instead prefer moving them intodescribe
andcontext
suites. This isolates the hooks, limiting them to the tests in that suite.
1 | console.log('support file') |
1 | context('spec a', () => { |
1 | context('spec b', () => { |
When we place the hooks into a suite like above, they apply correctly to the tests inside their suite.