NodeJs express MongoDB with jest error while executing test case - javascript

Previous Question Link
Open Question
Scenario
I've trying to test if my route for GET endpoint work or not which I've set correctly and I've tested by running server. But my test case gives me following error
Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
I've searched a bit and have tried all possible solutions which are stated but it still gives me same error.
Code
const request = require ('supertest');
const app = require ('../../app');
const db = require ('../../db.js');
const url = process.env.MONGO_URI || 'mongodb://localhost:27017'
beforeAll (done => {
db.connect (url, err => {
if (err) {
console.log ('Unable to connect', err);
process.exit (1);
}else{
console.log('Succesfully connected')
}
});
});
afterAll (done => {
db.close ();
});
test ('should response the GET method',done => {
const res = request (app).get ('/expense');
return res
.then (json => {
console.log ("Length",json.body.length);
expect (json.body.length).toBe (1, done ());
})
.catch (err => {});
},10000);
Test Output
● Console
console.log test/express/startupTest.test.js:12
Succesfully connected
console.log test/express/startupTest.test.js:26
Length 1
● should response the GET method
Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
at pTimeout (node_modules/jest-jasmine2/build/queueRunner.js:53:21)
at Timeout.callback [as _onTimeout] (node_modules/jsdom/lib/jsdom/browser/Window.js:523:19)
at ontimeout (timers.js:469:11)
at tryOnTimeout (timers.js:304:5)
at Timer.listOnTimeout (timers.js:264:5)
Test Suites: 1 failed, 2 passed, 3 total
Tests: 1 failed, 6 passed, 7 total
Snapshots: 1 passed, 1 total
Time: 6.58s

You need to invoke done callback after establishing connection with the DB.
beforeAll (done => {
db.connect (url, err => {
if (err) {
console.log ('Unable to connect', err);
process.exit (1);
}else{
console.log('Succesfully connected');
done();
}
});
});
Same with afterAll:
afterAll (done => {
db.close (() => done());
});
Also, you don't need to use done callback in test case, since you are returning a promise:
test ('should response the GET method', () => {
const res = request (app).get ('/expense');
return res
.then (json => {
console.log ("Length",json.body.length);
expect (json.body.length).toBe (1);
})
.catch (err => {});
});
When you return a promise from a test case, test resolution will be delayed until the promise resolves.

Related

How to write unit test for exponential backoff retry method implemented in javascript with jest

Trying to test an exponential backoff method which retries API request via fetch 5 times, will have the following delays: [1 ms, 10 ms, 100 ms, 1 s, 10 s], I am not able to successfully test it.
Methods
export const delay = retryCount => new Promise(resolve => setTimeout(resolve, 10 ** retryCount));
/**
* Fetching with delay when api call fails,
* first 5 retries will have the following delays: [1 ms, 10 ms, 100 ms, 1 s, 10 s]
*/
export const fetchRetry = async (options, retryCount = 0, lastError = null) => {
if (retryCount > 5) throw new Error(lastError);
try {
return await fetch(options);
} catch (error) {
await delay(retryCount);
return fetchRetry(options, retryCount + 1, error);
}
};
Test
import fetchMock from 'jest-fetch-mock';
import { delay, fetchRetry } from './retry';
// This can be set up globally if needed
fetchMock.enableMocks();
beforeEach(() => {
fetch.resetMocks();
});
describe('fetchWithExponentialBackoffRetry', () => {
it('fetch is called once when response is 200', done => {
fetch.mockResponseOnce(
JSON.stringify({
success: true,
message: 'OK',
code: 200,
data: 'c86e795f-fe70-49be-a8fc-6876135ab109',
}),
);
setTimeout(function() {
fetchRetry({
inventory_type_id: 2,
advertiser_id: 2315,
file: null,
});
expect(fetch).toHaveBeenCalledTimes(1);
done();
}, 0);
});
it('fetch is called 5 times when response is returns failure', done => {
fetch.mockReject(() => Promise.reject(new Error('Rejected')));
setTimeout(function() {
fetchRetry({
inventory_type_id: 2,
advertiser_id: 2315,
file: null,
});
expect(fetch).toHaveBeenCalledTimes(5);
done();
}, 100000);
});
});
I am getting the following error
console.error node_modules/jsdom/lib/jsdom/virtual-console.js:29
Error: Error: connect ECONNREFUSED 127.0.0.1:8
I think it has to do we delay method I have to incorporate setTimeout somehow in my tests, now sure how to mock it here. I would appreciate the help.
You're testing the outcome of an async function, so you need to make your tests async too - you're not doing so - i.e. you're not awaiting fetchRetry and are therefore just calling done() synchronously.
I think the error is being caused by the use of setTimeout here. This looks like a race condition bug so hard to be sure without debugging, but from reading the code it looks like the issue is that you're mocking fetch with jest-fetch-mock, but since your test code runs synchronously and you have...
beforeEach(() => {
fetch.resetMocks();
});
...it is probably unsetting the fetch mock before the call in the test that runs first is made and so it's actually calling your API - hence the error.
Making the tests async is fairly simple - the docs are here - and using async/await it's even cleaner since you don't actually need to use done - the test is just done when the promise resolves (or rejects).
Basically your test code will be mostly the same, except you'll be awaiting your calls to fetchRetry, like so:
it('fetch is called once when response is 200', async () => {
fetch.mockResponseOnce(...)
await fetchRetry({ ... })
expect(fetch).toHaveBeenCalledTimes(1);
});
it('fetch is called 5 times when response is returns failure', async () => {
fetch.mockReject(...);
try {
await fetchRetry({ ... });
} catch (err) {
// eventual error expected as response failure is mocked
expect(fetch).toHaveBeenCalledTimes(5);
}
});

Jest - How to test that console.error is called?

I am trying to write a unit test with jest/enzyme that tests if console.error() has been called in the catch() of a try/catch, but trying to do so either results in a successful test when it should be unsuccessful, or an "Expected mock function to have been called, but it was not called" error.
Function to test:
export const playSound = (soundName, extension = 'wav') => {
try {
SoundPlayer.onFinishedPlaying(success => success);
SoundPlayer.playSoundFile(soundName, extension);
} catch (err) {
console.error(`Error playing sound '${soundName}':`, err);
return err;
}
};
So the above takes a single argument soundName, which is a string, and I'm trying to test that a console error is logged when no argument is passed in.
I've most-recently tried the below, which seems to be miles off, and wrongfully returns a passed test.
it('fails to play sound with no method arguments', async () => {
const consoleSpy = jest
.spyOn(console, 'error')
.mockImplementation(() => {});
try {
playSound();
expect(consoleSpy).not.toHaveBeenCalled();
} catch (err) {
expect(consoleSpy).toHaveBeenCalled();
}
});
Your playSound function will never throw, because you're swallowing the exception.
you simply need this:
it('fails to play sound with no method arguments', async () => {
const consoleSpy = jest
.spyOn(console, 'error')
.mockImplementation(() => {});
playSound();
expect(consoleSpy).toHaveBeenCalled();
});
you can also check return value of the function call, which will be exception object.
Also if you want to check if function throws you can use
expect(function() { playSound(); }).toThrow();
but this will fail, unless you don't catch the exception or rethrow.

Appium error handling doesn't work

I'm newbie in appium so may do something wrong.
I've got an issue with appium using wdio and jasmine
it('wtf', (done) => {
client.init().element('someName').getText()
// ^ here was a mistake
.then(result => result, err => {
// ^ this error handling wasn't work
throw new Error(`Cannot get text of element: #someName\n${err}`);
})
.then(text => expect(text).toBe('correct'))
.then(done)
});
appium server log told me:
[HTTP] --> POST /wd/hub/session/63aa60d2-9638-4b1a-a226-cfbb2fcfce2c/element {"using":"css selector","value":"someName"}
[debug] [MJSONWP] Calling AppiumDriver.findElement() with args: ["css selector","someName","63aa60d2-9638-4b1a-a226-cfbb2fcfce2c"]
[debug] [BaseDriver] Valid locator strategies for this request: xpath, id, class name, accessibility id, -android uiautomator
[HTTP] <-- POST /wd/hub/session/63aa60d2-9638-4b1a-a226-cfbb2fcfce2c/element 500 4 ms - 152
[HTTP] --> POST /wd/hub/session {"desiredCapabilities":{"javascriptEnabled":true,"locationContextEnabled":true,"handlesAlerts":true,"rotatable":true,"platformName":"android","app":"./app-dev-debug.apk","appPackage":"com.#####.dev.debug","appActivity":"com.#####.feature.start.StartActivity","avdReadyTimeout":1000,"udid":"LGK350RGNBS4TS","deviceName":"LG-K350","clearSystemFiles":true,"fullReset":true,"newCommandTimeout":120,"requestOrigins":{"url":"http://webdriver.io","version":"4.12.0","name":"webdriverio"}}}
but jsmine stuck without throwed error, log:
Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
Stack:
Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
at ontimeout (timers.js:475:11)
at tryOnTimeout (timers.js:310:5)
at Timer.listOnTimeout (timers.js:270:5)
'done' wasn't call in promise
configured jasmine timeout = 300000
main question: Why Jasmine don't receive the throwed exception?
found solution (dummy fix):
Use function:
testMobile = <T>(promise: Client<T>, done: () => void): Client<void> =>
promise.then(done, err => {
expect(err).toBe('Ok');
done();
});
and use:
it('wtf', done =>
testMobile(client.init()
.element('someName').getText()
.then(result => result, err => {
throw new Error(`Cannot get text of element: #someName\n${err}`);
})
.then(text => expect(text).toBe('correct')),
done)
};

Expected one assertion to be called but received zero assertion calls

I am trying to test a method using Jest... The method should return Promise.reject() .
Here is the code I wrote:
test('testing Invalid Response Type', () => {
const client = new DataClient();
client.getSomeData().then(response => {
console.log("We got data: "+ response);
}).catch(e => {
console.log("in catch");
expect(e).toBeInstanceOf(IncorrectResponseTypeError);
});
expect.assertions(1);
});
When I run the test, it prints "in catch" but fails with this exception:
Expected one assertion to be called but received zero assertion calls.
console.log src/data/dataclient.test.js:25
in catch
● testing Invalid Response Type
expect.assertions(1)
Expected one assertion to be called but received zero assertion calls.
at extractExpectedAssertionsErrors (node_modules/expect/build/extract_expected_assertions_errors.js:37:19)
at <anonymous>
at process._tickCallback (internal/process/next_tick.js:188:7)
I solved it by adding return statement before the block.
With return statement the function will wait for catch block to finish.. and hence expect will be executed..
test('testing Invalid Response Type', () => {
const client = new DataClient();
return client.getSomeData().then(response => {
console.log("We got data: "+ response);
}).catch(e => {
console.log("in catch");
expect(e).toBeInstanceOf(IncorrectResponseTypeError);
});
expect.assertions(1);
});
You need to wait for the promise to finish to check number of assertions (to reach the .catch block).
see jest's asynchronous tutorial, specially the async/await solution. Actually, their example is almost identical to your problem.
in your example, you would do:
test('testing Invalid Response Type', async () => { // <-- making your test async!
const client = new DataClient();
await client.getSomeData().then(response => { // <-- await for your function to finish
console.log("We got data: "+ response);
}).catch(e => {
console.log("in catch");
expect(e).toBeInstanceOf(IncorrectResponseTypeError);
});
expect.assertions(1);
});
Btw, the accepted solution also works, but not suitable to multiple tests of async code

How to test a function which consumes a callback? Or how to defer the assertion?

I use Jest to test a function which generates a JSON Web Token. It seems that I can't assert the value since when I assert, the callback hasn't been executed yet.
const issueJWT = function issueJWT(req, res, next) {
jwt.sign(signUser, function (err, token) {
if (err) {
next(err);
return;
}
res.locals.token = token;
next();
});
};
This is my test, I mock the request and response, then assert the result:
test('Should return a JWT with proper value if nothing wrong happened', () => {
issueJWT(request, response, mockNext);
const JWT = response.locals.token;
const tokenPayload = jwt.decode(JWT, { complete: true }).payload;
expect(tokenPayload).toHaveProperty('iat');
expect(tokenPayload).toHaveProperty('exp');
expect(tokenPayload).toHaveProperty('id');
});
The error is:
TypeError: Cannot read property 'payload' of null
How to make it work?
According to my knowledge, I think the callback is at the task queue which
means it will be executed when nothing is in the event loop, right? I wanna find a way to defer my assertion, but don't know how...
Thanks for the tips, I use the done, now the test could pass, but the problem is, whenever there is a problem, the error message doesn't make any sense... Any problem to my solution?
test('Should return a JWT with proper value if nothing wrong happened', (done) => {
const callback = () => {
const JWT = response.locals.token;
const tokenPayload = jwt.decode(JWT, { complete: true }).payload;
expect(tokenPayload).toHaveProperty('iat');
expect(tokenPayload).toHaveProperty('exp');
expect(tokenPayload).toHaveProperty('id');
expect(tokenPayload).toHaveProperty('iss');
done();
};
issueJWT(request, response, callback);
});
The error is now:
Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
Ok, so with some help from #felixKling getting me to actually read the docs, you need to do something like this:
test('Should return a JWT with proper value if nothing wrong happened', done => {
issueJWT(request, response, (e) => {
const JWT = response.locals.token;
const tokenPayload = jwt.decode(JWT, { complete: true }).payload;
expect(tokenPayload).toHaveProperty('iat');
expect(tokenPayload).toHaveProperty('exp');
expect(tokenPayload).toHaveProperty('id');
done();
});
});
I'm not on my dev box so I can't test this, but basically the idea is that you use the 'done' parameter to the test callback to signal that the test is waiting on async code. The test framework will basically wait for your test to call that callback before exiting.
In this case, your next() call from issueJWT is what we're waiting on firing before checking to see if the various objects were updated. If you were not using next() in your middleware, you'd likely need to mock whatever response method you're calling instead (e.g. response.end()) to do your tests.

Categories