Mocha change timeout for afterEach - javascript

I am writing a node application with mocha and chai. Some of the tests call an external API for integration tests, which might take up to 90sec to perform the action.
In order to cleanup properly, I defined an afterEach()-block, which will delete any generated remote resources, in case an expect fails and some resources weren't deleted.
The tests themselves have an increased timeout, while the rest of the tests should retain their default and small timeout:
it('should create remote resource', () => {...}).timeout(120000)
However, I can't do the same with afterEach().timeout(120000), because the function does not exist - nor can I use the function ()-notation due to the unknown resource names:
describe('Resources', function () {
beforeEach(() => {
this._resources = null
})
it('should create resources', async () => {
this._resources = await createResources()
expect(false).to.equal(true) // fail test, so...
await deleteResources() // will never be called
})
afterEach(async() => {
this._resources.map(entry => {
await // ... delete any created resources ...
})
}).timeout(120000)
})
Any hints? Mocha is version 4.0.1, chai is 4.1.2

The rules are same for all Mocha blocks.
timeout can be set for arrow functions in Mocha 1.x with:
afterEach((done) => {
// ...
done();
}).timeout(120000);
And for 2.x and higher it, beforeEach, etc. blocks are expected to be regular functions in order to reach spec context. If suite context (describe this) should be reached, it can be assigned to another variable:
describe('...', function () {
const suite = this;
before(function () {
// common suite timeout that doesn't really need to be placed inside before block
suite.timeout(60000);
});
...
afterEach(function (done) {
this.timeout(120000);
// ...
done();
});
});
Mocha contexts are expected to be used like that, since spec context is useful, and there are virtually no good reasons to access suite context inside specs.
And done parameter or promise return are necessary for asynchronous blocks.

If you need to use dynamic context you have to use normal function.
describe('Resources', function () {
// ...
afterEach(function (){
this.timeout(120000) // this should work
// ... delete any created resources ...
})
})

Related

Jest mock different return values for a function in each test

I want to have different values returned for the same mock in each Test.
I am using mockReturnValueOnce for returning different values in each call of a test.
But for further test when I use mockImplementationOnce it still returns mockReturnValue.
const mock = require('module-to-mock');
jest.mock('module-to-mock');
describe('tests', () => {
it('test #1', async () => {
mock.foo = jest
.fn()
.mockReturnValueOnce(1) // First call
.mockReturnValueOnce(2) // Seconds call
.mockReturnValue("default"); // Rest of the calls
// execute test
});
it('test #2', async () => {
mock.foo.mockImplementationOnce(() => 3);
// It is not working I am getting "default"
// execute test
});
});
I did try resetting the mock using mock.foo.mockReset(), but it did not help. I did add it at the end of each test, once the test is executed. I also try by adding at the beginning of each test.
I have common mock implementation for all the functions in module-to-mock in a separate file.
I just want to remove/reset the mock done in the test #1 and move to the original mock done in a separate file.

Why doesn't Jest.useFakeTimers work in beforeAll blocks?

I wanted to mock timeout functions globally so I added a call to jest.useFakeTimers in a beforeAll block. When I ran my tests, jest.advanceTimersByTime didn't execute the scheduled code. I used spies to confirm that the mock was indeed being called. I noticed however, that if I moved the jest.advanceTimersByTime call into the test function, or into a beforeEach block, it worked.
Here's a simple example that reproduces the problem.
describe('test', () => {
beforeEach(() => {
// Only beforeEach block works, NOT beforeAll
jest.useFakeTimers();
})
it('setTimeout calls callback', () => {
const callback = jest.fn();
setTimeout(() => {
callback()
}, 3000);
expect(callback).not.toHaveBeenCalled();
jest.advanceTimersByTime(3000);
expect(callback).toHaveBeenCalledTimes(1);
})
})
I didn't see anything in the docs that explains this. I'm using Jest 26.6.2.
Seems like jest.useFakeTimers in the documentation replaces setTimeout in product code, not within the test code.
Maybe you could check your jest setup file(like setupFilesAfterEnv config). Say, If I put those codes in the setup file, then it will run into the problem you said above:
beforeEach(() => {
// reset to real timers
jest.useRealTimers();
})
Because as default behavior, beforeEach's callback is always executed after beforeAll's, even if it is defined before beforeAll.

How do I properly test using a mocked service that returns a Promise?

I'm new to Angular and I'm still trying to figure out how things work for it. I'm currently having trouble testing a Component that depends on a Service that returns a Promise. The function I'm testing is structured like the following:
success: boolean;
borrowBook() {
this.bookService.borrow(this.selectedBook.id)
.then(() => {
this.success = true;
})
.catch((error: BorrowingError) => {
this.success = false;
});
}
Now, I'm not really sure if something like the code above is considered idiomatic, but that's how I wrote the code. In my unit test, I mocked the bookService using jasmine.createSpyObj function, and defined the stub as follows:
mockBookService.borrow.and.returnValue(Promise.resolve(testResult));
However, my test fails saying that component.success is undefined when I expected it to be truthy. My test is programmed as follows:
it('test', async(() => {
mockBookService.borrow.and.returnValue(Promise.resolve(testResult));
//setup pre-conditions here...
component.borrowBook(();
expect(component.success).toBeTruthy();
}));
My impression is that the expectations are being checked even before the Promise is handled accordingly.
I chanced upon this article about testing asynchronous code in Angular, which stated that the flushMicrotasks function can be used to run all the async components before checking expectations. This is only provided through the fake zone created through the fakeAsync, so in my code I used that instead of just async.
it('test', fakeAsync(() => { //used fakeAsync instead
mockBookService.borrow.and.returnValue(Promise.resolve(testResult));
//setup pre-conditions here...
component.borrowBook(();
flushMicrotasks(); //added this before checking expectations
expect(component.success).toBeTruthy();
}));

Wait for an own function (which returns a promise) before tests are executed

I'm new to cypress and am trying to figure out how things work.
I have my own function (which calls a test controller server to reset the database). It returns a promise which completes when the DB have been successfully resetted.
function resetDatabase(){
// returns a promise for my REST api call.
}
My goal is to be able to execute it before all tests.
describe('Account test suite', function () {
// how can I call resetDb here and wait for the result
// before the tests below are invoked?
it('can log in', function () {
cy.visit(Cypress.config().testServerUrl + '/Account/Login/')
cy.get('[name="UserName"]').type("admin");
cy.get('[name="Password"]').type("123456");
cy.get('#login-button').click();
});
// .. and more test
})
How can I do that in cypress?
Update
I've tried
before(() => {
return resetDb(Cypress.config().apiServerUrl);
});
But then I get an warning saying:
Cypress detected that you returned a promise in a test, but also invoked one or more cy commands inside of that promise
I'm not invoking cy in resetDb().
Cypress have promises (Cypress.Promise), but they are not real promises, more like duck typing. In fact, Cypress isn't 100% compatible with real promises, they might, or might not, work.
Think of Cypress.Promise as a Task or an Action. They are executed sequentially with all other cypress commands.
To get your function into the Cypress pipeline you can use custom commands. The documentation doesn't state it, but you can return a Cypress.Promise from them.
Cypress.Commands.add('resetDb', function () {
var apiServerUrl = Cypress.config().apiServerUrl;
return new Cypress.Promise((resolve, reject) => {
httpRequest('PUT', apiServerUrl + "/api/test/reset/")
.then(function (data) {
resolve();
})
.catch(function (err) {
reject(err);
});
});
});
That command can then be executed from the test itself, or as in my case from before().
describe('Account', function () {
before(() => {
cy.resetDb();
});
it('can login', function () {
// test code
});
})
You can use cy.wrap( promise ), although there might still be a bug where it never times out (haven't tested).
Otherwise, you can use cy.then() (which is undocumented, can break in the future, and I'm def not doing any favors by promoting internal APIs):
cy.then(() => {
return myAsyncFunction();
});
You can use both of these commands at the top-level of spec like you'd use any command and it'll be enqueued into cypress command queue and executed in order.
But unlike cy.wrap (IIRC), cy.then() supports passing a callback, which means you can execute your async function at the time of the cy command being executed, not at the start of the spec (because expressions passed to cy commands evaluate immediately) --- that's what I'm doing in the example above.

Async Mocha done() isn't called in my function

I call this function, console log is called but done() is not called:
import {Database} from "../../code/server/Database";
import 'mocha'
const expect = require('chai').expect
describe('Database save', function() {
it('should save without error', function(done) {
Database.saveSessionData(1, 2, 3, function(err, docs) {
expect(err).to.equal(null)
expect(docs.sessionOpenTime).to.equal(1)
expect(docs.sessionCloseTime).to.equal(2)
expect(docs.sessionLength).to.equal(3)
console.log(2222)
done()
})
})
})
Here is the result, 'Running tests' continues spinning on forever:
But if I change the 'Database' code into this, it works as expected:
setTimeout(function () {
console.log('lol')
done()
}, 1000)
What am I missing here?
Mocha test hangs since you have an opened database connection.
There are two options to solve this problem:
If you do not need a real database connection in your tests:
you can use sinon.stub() (https://www.npmjs.com/package/sinon) to return a predetermined response for async methods you use in your tests or sinon.spy() to make sure a stubbed method called exact number of times.
Here's a good article I just found to illustrate how to use sinon.js: https://semaphoreci.com/community/tutorials/best-practices-for-spies-stubs-and-mocks-in-sinon-js.
you can implement a dependency injection container to be able to replace your implementation of Database class to a Database class that does not perform I/O operations.
Although dependency injection implementations may vary depending on requirements some simple implementations are also available:
https://blog.risingstack.com/dependency-injection-in-node-js/
If you need to perform a real connection in your tests:
Consider adding an after hook to your mocha tests:
let's say mongodb is used as a database (it does not matter, but it would
be an actual working example)
const mongoose = require('mongoose');
const expect = require('chai').expect;
mongoose.connect('mongodb://localhost/test');
after(() => {
mongoose.connection.close();
});
describe('db connection', () => {
it('should make a test', () => {
expect(1).to.equal(1);
});
});

Categories

Resources