How to run Jest-Puppeteer test in multiple files - jestjs

I am using jest-puppeteer to run my webtests. If I am running my tests all defined in one file, everything works perfectly.
describe('user', () => {
jest.setTimeout(12000);
beforeEach(async () => {
await page.setViewport({width: 1200, height: 2000});
await page.goTo('http://localhost:3000');
});
it('test 1', async () => {
//my test steps
});
it('test 2', async () => {
//my test steps
});
});
However, if I am running each test in its own file, I get an error.
UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'addExpectationResult' of undefined
FILE 1
describe('user', () => {
jest.setTimeout(12000);
beforeEach(async () => {
await page.setViewport({width: 1200, height: 2000});
await page.goTo('http://localhost:3000');
});
it('test 1', async () => {
//my test steps
});
});
FILE 2
describe('user', () => {
jest.setTimeout(12000);
beforeEach(async () => {
await page.setViewport({width: 1200, height: 2000});
await page.goTo('http://localhost:3000');
});
it('test 2', async () => {
//my test steps
});
});
Interestingly, if I add a console.log('some statement') as first step in test2, everything works again. That's why I think it might be a timing issue. I am running my tests sequentially, i.e. jest --runInBand
Can anyone help?

As you say i agree that the problem is the async sequentially, as explained in that github bugreport in Patrick Hulce answer:
FWIW I also ran into this and was led to this issue. Only happened in
CI when an async test timed out but the underlying action continued
until after the test suite finished (and I'm guessing the callback
tried to add the result of the expectation). I could never get it to
happen locally though.
so probably your error is inside "//my test steps" where you manipulate something in both the test that you can/need manipulate before running the async process.

Related

Jest run code before and after all of the tests

I would like to run code before and after all the tests, not file wide, test wide. For example;
Before starting the e2e tests, I would like to run the server in test mode with the database. After all, I would like to flush my db and close the processes.
I don't know if it is possible but I also would like to have a global db variable to do tests. What I am currently doing is like this:
describe("Posts Module", () => {
let dbService: DatabaseService;
beforeAll(async () => {
dbService = new DatabaseService();
await dbService.init();
});
it("should give the posts", () => {
supertest(app)
.get("/posts")
.expect(200)
.then(async (response) => {
const dbPosts = await dbService.getPosts();
expect(response.body).toBeDefined();
expect(response.body.posts).toEqual(dbPosts);
});
});
afterAll(async () => {
await flushDb(dbService);
await dbService.close();
});
});
But what I actually want is initing this database service only once before all of the module tests (also starting the server, currently I start the server manually and run the tests afterwards).

How to use jest.each asynchronously

I am having problems loading filenames into jest.each asynchronously.
My code:
let files: string[][]
function getFilesWorking() {
files = [["test1"], ["test2"]]
}
async function getFilesAsync() {
files = await Promise.resolve([["test1"], ["test2"]])
}
beforeAll(() => {
console.log("before")
})
describe.only("Name of the group", () => {
getFilesAsync()
test.each(files)("runs", f => {})
})
beforeAll is executed before each test but NOT before initialization of test.each, so I end up with undefined.
How can I load files before using test.each?
You can pass an async callback to beforeAll and await getFilesAsync within it
beforeAll(async () => {
await getFilesAsync();
})
As of Jest 28.1.3 and prior, this is not possible. There is an open issue documenting this behavior.
The best thing you can do for now is put your tests in a regular it() test and do a deep value comparison:
it('tests an array of cases', async () => {
const data = await getSomeAsyncData()
const expectedData = [ ... ]
expect(data).toEqual(expectedData)
})
You can use beforeEach to set up code that will run prior to tests for any given scope, https://jestjs.io/docs/setup-teardown:
beforeEach(() => {
console.log('before every test');
});
describe.only(('Name of the group') => {
beforeEach(() => {
console.log('before tests in this describe block');
})
})
Jest is only going to run the tests in your describe.only block. If you want to use beforeEach in other blocks and run those tests as well, change describe.only to describe.
(Edit: I know this is a year late, I'm just trying to look for a similar problem/solution set and thought I could answer this.)

Nodejs Jest mocking is not working in multiple describe block in a single file, it works in first describe block only

I am using jest for unit testing in my node express application,please excuse , because i am new to all this
in my abc.test.js
const s3Helper = require('../../../../../lib/s3_helper');
beforeEach(async () => {
s3Helper.uploadBufferToS3 = jest.fn(() => true);
});
describe('test1', () => {
it('test1', async () => {
expect(s3Helper.uploadBufferToS3)
.toHaveBeenCalled();
});
});
describe('test2', () => {
it('test2', async () => {
expect(s3Helper.uploadBufferToS3)
.toHaveBeenCalled();
});
});
so when i run this test file in test1 it returns that test is passed, however in test2 it returns expected >=1 returned 0.
since i am mocking it beforeEach i expect it should return 1 for each describe block

Mocha testing inside async callbacks

I have simplified the example to be able to explain it well. I have an array which I want to iterate on. For each element of the array I want to execute a test with async/await functions, so I have this code:
const chai = require('chai');
const expect = chai.expect;
describe('Each film', async() => {
await Promise.all([1, 2, 3].map(async(n) => {
await new Promise(resolve => setTimeout(() => resolve(), 1000));
console.log('N:', n);
it('test', async() => {
expect(true).to.be.true;
});
}));
});
Executing this results in the following output:
0 passing (1ms)
N: 1
N: 2
N: 3
However, if I don't use async/await it is executed as I would expect, so it generates three tests that are resolved correctly.
What could be happening here?
UPDATE
I finally discovered that setTimeout can be used to load data asynchronously and then generate tests dinamically. This is the explanation from mocha page:
If you need to perform asynchronous operations before any of your suites are run, you may delay the root suite. Run mocha with the --delay flag. This will attach a special callback function, run(), to the global context:
So I finally wrote the code this way:
const chai = require('chai');
const expect = chai.expect;
setTimeout(async() => {
await Promise.all([1, 2, 3].map(async(n) => {
describe(`Element number ${n}`, () => {
it('test', async() => {
await new Promise(resolve => setTimeout(() => resolve(), 1000));
expect(true).to.be.true;
});
});
}));
run();
}, 500);
which generates the following output:
➜ node_modules/.bin/mocha --delay test.js
Element number 1
✓ test (1005ms)
Element number 2
✓ test (1001ms)
Element number 3
✓ test (1002ms)
3 passing (3s)
Mocha does not support asynchronous describe functions. You can generate tests dynamically, as described here, but that generation must still be synchronous.
Any tests that aren't created synchronously won't be picked up by the runner. Hence the 0 passing line at the top of your output. Mocha has decided there are no tests well before your promise resolves.
This isn't to say testing your stuff is impossible, just that you need to rethink how you're using Mocha to test it. The following, for example, would be similar to loading all of your things up front and making an assertion on each one in various tests:
const chai = require('chai');
const expect = chai.expect;
describe('Each item', () => {
let items;
before(async () => {
items = [];
await Promise.all([1, 2, 3].map(async(n) => {
await new Promise(resolve => setTimeout(() => resolve(), 1000));
items.push(n);
}));
})
it('is a number', () => {
for (item of items) {
expect(item).to.be.a('number');
}
});
it('is an integer', () => {
for (item of items) {
expect(item % 1).to.equal(0)
}
});
it('is between 1 and 3', () => {
for (item of items) {
expect(item).to.be.within(1, 3)
}
});
});
Unfortunately you won't be able to make a fully separate test displaying in your output for each item. If you want this, you may check out another test runner. I don't really have enough experience with others to say whether or not any of them support this. I'd be surprised if they do, though, since it's quite unusual.

Mocha tests with async initialization code

I am writing tests for a REST client library which has to "login" against the service using the OAuth exchange. In order to prevent logging in for every endpoint I am going to test I'd like to write some sort of "test setup" but I am not sure how I am supposed to do this.
My test project structure:
test
endpoint-category1.spec.ts
endpoint-category2.spec.ts
If I had only one "endpoint category" I had something like this:
describe('Endpoint category 1', () => {
let api: Client = null;
before(() => {
api = new Client(credentials);
});
it('should successfully login using the test credentials', async () => {
await api.login();
});
it('should return xyz\'s profile', async () => {
const r: Lookup = await api.lookup('xyz');
expect(r).to.be.an('object');
});
});
My Question:
Since the login() method is the first test there, it would work and the client instance is available for all the following tests as well. However, how can I do some sort of setup where I make the "logged in api instance" available to my other test files?
Common code should be moved to beforeEach:
beforeEach(async () => {
await api.login();
});
At this point should successfully login using the test credentials doesn't make much sense because it doesn't assert anything.
describe('Endpoint category 1', () => {
let api: Client = null;
beforeEach(() => {
api = new Client(credentials);
});
afterEach(() => {
// You should make every single test to be ran in a clean environment.
// So do some jobs here, to clean all data created by previous tests.
});
it('should successfully login using the test credentials', async () => {
const ret = await api.login();
// Do some assert for `ret`.
});
context('the other tests', () => {
beforeEach(() => api.login());
it('should return xyz\'s profile', async () => {
const r: Lookup = await api.lookup('xyz');
expect(r).to.be.an('object');
});
});
});
Have you had a look at https://mochajs.org/#asynchronous-code ?
You can put in a done-parameter in your test functions and you will get a callback with this you have to call.
done() or done(error/exception)
This done would be also available in before and after.
When calling done() mocha knows your async-code has finished.
Ah. And if you want to test for login, you shouldn't provide this connection to other tests, because there is no guarantee of test order in default configuration.
Just test for login and logout afterwards.
If you need more tests with "login-session", describe a new one with befores.

Resources