How Nested test execute in parallalization in Cypress - cypress

if I have nested describes
describe('Desc1', () => {
describe('Desc1.1', () => {
});
describe('Desc1.2', () => {
});
});
then when I run it in parallelization
is the Desc1, Desc 1.1, and Desc 1.2 will be distributed to machines or will they executes in the same machine
just wanted to check if it's going to save some time or not in case of nested test suits

Related

Cypress: Using same fixture file in multiple test

I need to pass the url and other variable in multiple tests[it-function]. For 1st test code run successfully but for 2nd test it showing error. Is there any workaround or solution? My code is as follows
describe('Document Upload', function()
{
before(function () {
cy.fixture('Credential').then(function (testdata) {
this.testdata = testdata
})
})
//1st test
it('Login as manager',function()
{
const login = new loginPage()
cy.visit(this.testdata.baseUrl);
login.getUserName().type(this.testdata.userDocumentM)
login.getPassword().type(this.testdata.passwordDocumentM)
login.getLoginButton().click()
//Logout
login.getUser().click()
login.getLogout().click()
})
//2nd test
it('Create Documents',function()
{
const login = new loginPage()
cy.visit(this.testdata.baseUrl);
login.getUserName().type(this.testdata.userDocumentM)
})
})
The error is
I have tried with above and also using before function again but same error
before(function () {
cy.fixture('Credential').then(function (testdata) {
this.testdata = testdata
})
})
//2nd test
it('Create Documents',function()
{
const login = new loginPage()
cy.visit(this.testdata.baseUrl);
login.getUserName().type(this.testdata.userDocumentM)
})
Starting with Cypress version 12 Test Isolation was introduced. This now means the Mocha context (aka this) is completely cleaned between tests.
Mocha context
It used to be (undocumented) that the Mocha context could be used to preserve variables across tests, for example
before(function () {
cy.fixture("example.json").as('testdata') // preserved on "this"
})
it("check testdata 1", function () {
expect(this.testdata).to.not.be.undefined // passes
})
it("check testdata 2", function () {
expect(this.testdata).to.not.be.undefined // fails in Cypress v12
})
but now that does not work.
The use of Mocha context is a bit arbitrary anyway, and requires explicit use of function-style functions which is easy to forget, particularly in places like array method callbacks Array.forEach(() => {}).
You can still use the Cypress context to store data
before(function () {
cy.fixture("example").then(function (testdata) {
Cypress.testdata = testdata;
})
})
it("check testdata 1", function () {
expect(Cypress.testdata).to.not.be.undefined // passes
})
it("check testdata 2", function () {
expect(Cypress.testdata).to.not.be.undefined // passes
})
Note this is also undocumented and may change in the future.
Caching methods
Technically, the way to do this is to set the alias with beforeEach().
The cy.fixture() command caches it's value, so you do not get the read overhead for each test (see Fixture returns outdated/false data #4716)
There is also cy.session() for more complicated scenarios, which would be officially supported.
beforeEach(() => {
cy.session('testdata', () => {
cy.fixture("example").then(testdata => {
sessionStorage.setItem("testdata", testdata)
})
})
})
it("check testdata 1", function () {
expect(sessionStorage.getItem("testdata")).to.not.be.undefined
})
it("check testdata 2", function () {
expect(sessionStorage.getItem("testdata")).to.not.be.undefined
})
Lastly, cypress-data-session which fills a gap
From the docs
Feature
cy.session
cy.dataSession
Command is
official ✅
community 🎁
Can cache
the browser session state
anything
Stability
experimental !!! not in v12
production
Cache across specs
yes
yes
Access to the cached data
no ???
yes
Custom validation
no
yes
Custom restore
no
yes
Dependent caching
no
yes
Static utility methods
limited
all
GUI integration
yes
no
Should you use it?
maybe
yes
Cypress version support
newer versions
all
Cypress.env()
This is another way that is officially supported,
before(() => {
cy.fixture("example").then(testdata => {
Cypress.env("testdata", testdata)
})
})
it("log my fixture 1", function () {
expect(Cypress.env("testdata")).to.not.be.undefined // passes
})
it("log my fixture 2", function () {
expect(Cypress.env("testdata")).to.not.be.undefined // passes
})
but there are still certain tests scenarios that reset the browser completely where this may not work.

Turn off clearing aliases in Cypress test

We have a number of variables from the API which are used throughout the tests. The values are saved to aliases at various points.
How can we turn off clearing aliases between test?
If we use one big test this works fine but Cypress by default clears aliases between tests, so as we break down the tests into smaller more manageable units we are breaking the code.
Simple example:
before(() => {
cy.wrap(123).as('alias')
})
it('test1', () => {
// alias is usable here
cy.get('#alias').should('eq', 123)
cy.wrap(456).as('alias2')
});
it('test2', () => {
// alias is missing here
cy.get('#alias').should('eq', 123)
cy.get('#alias2').should('eq', 456)
});
There's no configuration to turn off alias cleardown.
You would have to create a custom command to do it.
Cypress.Commands.add('keepAliases', function(aliasList) {
if (!aliasList) {
aliasList = Object.keys(this)
.filter(key => !['test', '_runnable', 'currentTest']
.includes(key))
}
aliasList.forEach(key => {
cy.wrap(this[key]).as(key)
})
})
Usage for a single test
before(() => {
cy.wrap(123).as('alias')
})
it('test1', () => {
cy.get('#alias').should('eq', 123)
cy.wrap(456).as('alias2')
});
// ✅ test passes
it('test2', () => {
cy.keepAliases()
cy.get('#alias').should('eq', 123)
cy.get('#alias2').should('eq', 456)
});
Usage for all tests
before(() => {
cy.wrap(123).as('alias')
})
beforeEach(() => {
cy.keepAliases()
})
it('test1', () => {
cy.get('#alias').should('eq', 123)
cy.wrap(456).as('alias2')
});
// ✅ test passes
it('test2', () => {
cy.get('#alias').should('eq', 123)
cy.get('#alias2').should('eq', 456)
});
According to cypress doc
aliases and properties are automatically cleaned up after each test
so with current cypress versions your goal is not achievable.
As a workaround you could use cypress.env as a storage for your tests:
Cypress.env(varName, varValue)
and using a test0 to reset all variables before each texts execution:
it('test0', () => {
// clean up all stored aliases
cy.env("var1",null);
cy.env("var2",null);
//...
});

Do jest tests wait for previous async tests to resolve?

I have read that jest tests in the same tests file execute sequentially. I have also read that when writing tests that involve callbacks a done parameter should be used.
But when using promises using the async/await syntax that I am using in my code below, can I count on the tests to but run and resolve in sequential order?
import Client from '../Client';
import { Project } from '../Client/types/client-response';
let client: Client;
beforeAll(async () => {
jest.setTimeout(10000);
client = new Client({ host: 'ws://127.0.0.1', port: 8080 , logger: () => {}});
await client.connect();
})
describe('Create, save and open project', () => {
let project: Project;
let filename: string;
beforeAll(async () => {
// Close project
let project = await client.getActiveProject();
if (project) {
let success = await client.projectClose(project.id, true);
expect(success).toBe(true);
}
})
test('createProject', async () => {
project = await client.createProject();
expect(project.id).toBeTruthy();
});
test('projectSave', async () => {
filename = await client.projectSave(project.id, 'jesttest.otii', true);
expect(filename.endsWith('jesttest.otii')).toBe(true);
});
test('projectClose', async () => {
let success = await client.projectClose(project.id);
expect(success).toBe(true);
});
test('projectOpen', async () => {
project = await client.openProject(filename);
expect(filename.endsWith('jesttest.otii')).toBe(true);
});
})
afterAll(async () => {
await client.disconnect();
})
From the docs:
...by default Jest runs all the tests serially in the order they were encountered in the collection phase, waiting for each to finish and be tidied up before moving on.
So while Jest may run test files in parallel, by default it runs the tests within a file serially.
That behavior can be verified by the following test:
describe('test order', () => {
let count;
beforeAll(() => {
count = 0;
})
test('1', async () => {
await new Promise((resolve) => {
setTimeout(() => {
count++;
expect(count).toBe(1); // SUCCESS
resolve();
}, 1000);
});
});
test('2', async () => {
await new Promise((resolve) => {
setTimeout(() => {
count++;
expect(count).toBe(2); // SUCCESS
resolve();
}, 500);
});
});
test('3', () => {
count++;
expect(count).toBe(3); // SUCCESS
});
});
For sure it depends on test runnner configured. Say for Jasmine2 it seems impossible to run tests concurrently:
Because of the single-threadedness of javascript, it isn't really possible to run your tests in parallel in a single browser window
But looking into docs' config section:
--maxConcurrency=
Prevents Jest from executing more than the specified amount of tests at the same time. Only affects tests that use test.concurrent.
--maxWorkers=|
Alias: -w. Specifies the maximum number of workers the worker-pool will spawn for running tests. This defaults to the number of the cores available on your machine. It may be useful to adjust this in resource limited environments like CIs but the default should be adequate for most use-cases.
For environments with variable CPUs available, you can use percentage based configuration: --maxWorkers=50%
Also looking at description for jest-runner-concurrent:
Jest's default runner uses a new child_process (also known as a worker) for each test file. Although the max number of workers is configurable, running a lot of them is slow and consumes tons of memory and CPU.
So it looks like you can configure amount of test files running in parallel(maxWorkers) as well as concurrent test cases in scope of single worker(maxConcurrency). If you use jest as test runner. And this affects only test.concurrent() tests.
For some reason I was unable to find anything on test.concurrent() at their main docs site.
Anyway you can check against your environment by yourselves:
describe('checking concurrent execution', () => {
let a = 5;
it('deferred change', (done) => {
setTimeout(() => {
a = 11;
expect(a).toEqual(11);
done();
}, 1000);
});
it('fails if running in concurrency', () => {
expect(a).toEqual(11);
});
})
Sure, above I used Jasmine's syntax(describe, it) so you may need to replace that with other calls.

How to detect all tests (in multiple files) finished

I would like to drop database after all tests in all files ran. Is there a hook for it in Mocha?
after() hook can be applied only within 1 file only.
There is a root level hook in mocha. For your case, you can create a new file and specify drop database command in after function. That's it!
// init-test.js
after(function() {
// drop database
});
You don't need to move any test in other test file for this solution.
Reference:
https://mochajs.org/#root-level-hooks
Create a parent file that includes/requires all other tests, then use after within that file:
describe('User', () => {
require('../user/user.spec.js')
})
describe('Post', () => {
require('../post/post.spec.js')
})
describe('Tag', () => {
require('../tag/tag.spec.js')
})
describe('Routes', () => {
require('./routes.spec.js')
})
after(() => {
// drop database
})
I'm using process.on('exit'). It works when running only one file as well as when running tests from package.json.
database.mocha-base.js:
db.connect()
process.on('exit', async () => {
console.log('All tests finished. Droping database')
db.dropDatabase(dbName)
db.disconnect()
})
module.exports = {
applyHooks() {
before(async () => {
// truncate tables
})
}
}
I'm including database.mocha-base.js in all tests that need database access:
const dbMochaBase = require('./path/to/database.mocha-base.js')
describe('Tests', () => {
dbMochaBase.applyHooks()
...
})

Run function after beforeEach

I need to run a function before my tests, but after all beforeEaches. Is there a way of prioritise beforeEaches, reorder them or monkeypatching something so that I can run a function before test gets executed?
Reason: I want to count how many database calls my tests do, but not include those that beforeEach calls.
Mocha executes the beforeEach hooks that pertain to a test in the order in which it encountered them in your code, so there should not be a need to monkeypatch. For instance:
describe("top", () => {
beforeEach(() => {
console.log("A");
});
describe("down", () => {
beforeEach(() => {
console.log("B");
});
beforeEach(() => {
console.log("C");
// Put your code here.
});
it("test 1", () => {});
it("test 2", () => {});
});
});
The above outputs:
top
down
A
B
C
✓ test 1
A
B
C
✓ test 2

Resources