I have a confusing situation on my tests. See the code below.
file.js
class Test {
constructor() {}
async main() {
const a = await this.aux1("name1");
const b = await this.aux2("name2");
}
async aux1(name) {
console.log(name)
}
async aux2(name) {
console.log(name)
}
}
module.exports = Test;
file.spec.js
describe('Concept proof', () => {
const module = require("./file.js");
const inst = new module();
test('should call aux1', async () => {
const a = jest.fn(() => "return");
inst.aux1 = a
inst.main()
expect(a).toHaveBeenCalled()
});
test('should call aux2', async () => {
const b = jest.fn(() => "return");
inst.aux2 = b
inst.main()
expect(b).toHaveBeenCalled()
});
});
result
expect(jest.fn()).toHaveBeenCalled()
Expected number of calls: >= 1
Received number of calls: 0
18 | inst.main()
19 |
> 20 | expect(b).toHaveBeenCalled()
| ^
21 | });
22 | });
at Object.<anonymous> (file.spec.js:20:19)
The method aux2 is not invoked if aux1 is called from main, but behave as expected if I remove aux1 call from main.
I was not able to find an explanation for that behave on the docs. I'm misunderstanding something, or the normal behave should be aux2 be called even if aux1 is called before?
Any help will be appreciated!
Any help will be apreciated!
You should use await inst.main();.
inst.main() is an asynchronous method. If await is not used, the main thread will continue to execute and will not wait for the completion of inst.main().
This means the expect(b).toHaveBeenCalled() statement will be executed before inst.main() method.
Related
this is the code to initialize a gRPC server I am using:
export const initServer = async (finalPort: number): Promise<string> => {
let initStatus = 'initial';
gRPCserver.addService(webcomponentHandler.service, webcomponentHandler.handler);
// define the host/port for server
await gRPCserver.bindAsync(
`localhost:${finalPort}`,
grpc.ServerCredentials.createInsecure(),
(err: Error | null, port1: number) => {
if (err != null) {
console.error(err);
log.info('Biding Error');
initStatus = 'Binding Error';
}
// start the gRPC server
gRPCserver.start();
log.info(`startServer::gRPC server started, on port: ${port1}`);
initStatus = 'OK';
},
);
return initStatus;
};
I am trying to return that initStatus to this function:
type StartServerType = () => Promise<string | undefined>;
export const startServer: StartServerType = async (): Promise<string | undefined> => {
try {
let initStatusResponse;
// Get port
const finalPort = port();
await server.initServer(finalPort).then((res) => {
initStatusResponse = res;
log.info('INIT STATUS RESPONSE: ', initStatusResponse);
});
} catch (e: unknown) {
if (typeof e === 'string') {
return e.toUpperCase(); // works, `e` narrowed to string
}
if (e instanceof Error) {
return e.message; // works, `e` narrowed to Error
}
}
return 'started';
};
But I always received 'initial' as initStatusResponse. I know there is an async issue there, but I can not see where.
Thanks in advance.
You will need to check the documentation for gRPCserver.bindAsync but it would seem that this method does not return a promise object. The likelihood of this is increased because you are passing a callback function as an argument. Promise objects remove the need for callback functions (Often referred to as "callback hell"). Here is an article that explains the differences between the different ways to do asynchronous actions in Javascript/Typescript.
Using async/await is a good approach. In order to use it if gRPCserver.bindAsync doesn't return a promise we can "wrap" it in a promise. This can be done easily with the node.js util promisify (Documentation) or manually with new Promise. This article explains both ways well.
As suggested in this post , I tried the steps to create dynamic tests , but I see the actual test(test.getMochaTest()in my below implementation) not getting executed. What's that I'm missing here, the call on test.getMochaTest() does not get executed in the before block.
describe('Dynamic Tests for Mocha', async () => {
let outcome ;
before(async() => {
await init().catch(() => console.error('Puppeteer environment initialization failed'));
return collectTests().then(async(collectTests) => {
console.info('4.Executing tests :');
describe('Dynamic test execution', async() => {
collectTests.forEach(async(test) => {
console.info(`\tModule under test : ${test.name}`);
// impl. of test.getMochaTest() DOES NOT get executed.
it(test.name, async() => outcome = await test.getMochaTest().catch(async () => {
console.error(`error while executing test:\t${test.name}`);
}));
});
}) ;
});
});
after(async () => {
console.info('5. Exiting tests...');
await HelperUtils.delay(10000).then(async () => { await browser.close(); });
console.log('executing after block');
});
it('placeholder', async() => {
await
console.log('place holder hack - skip it');
});
});
Array of tests is returned here :
async function collectTests():Promise<Array<ITest>> {
console.info('3.Collecting tests to execute ...');
testArray = new Array<ITest>();
const login:ITest = new SLogin('Login Check');
testArray.push(login);
return testArray;
}
The below implementation of getMochaTest in SLogin -> does not get executed .
export default class SLogin extends BTest implements ITest {
constructor(name: string) {
super(name);
}
async getMochaTest():Promise<Mocha.Func> {
return async () => {
console.log('Running Login check');
expect(true).to.equal(true);
};
}
}
It doesn't look like you're actually invoking the test.
Calling test.getMochaTest() only returns the async test function in a Promise, it doesn't execute it. So your catch block is catching errors while obtaining the function, not while executing it.
Breaking it out across multiple lines will hopefully make things clearer.
Here's what your code sample does. Notice it never executes the returned test function:
it(test.name, async () => {
const testFn = await test.getMochaTest().catch(() =>
console.error(`error while ***obtaining*** test: \t${test.name}`));
// oops - testFn never gets called!
});
And here's a corrected version where the test actually gets called:
it(test.name, async () => {
const testFn = await test.getMochaTest().catch(() =>
console.error(`error while ***obtaining*** test: \t${test.name}`));
const outcome = await testFn().catch(() =>
console.error(`error while ***executing*** test: \t${test.name}`));
});
Note: I wrote it that way with await and catch() to better match the format of your code sample. However, it's worth pointing out that it mixes async/await and Promise syntax. More idiomatic would be to catch errors with a try/catch block when using async/await.
Will be grateful if someone could clarify to me how test the async code from inquirer plugin for CLI app.
The module exports updateView function, which calls async inquirer.prompt inside.
const inquirer = require("inquirer");
const getAnswer = async (request) => {
const answer = await inquirer.prompt(request);
return answer;
}
Want to test with Jest that async code works, however all the Jest examples I have seen show ways to test async code only if I pass async function as a parameter.
So my function will have to be refactored to that:
getAnswers.js
const getAnswer = async (request, callback) => {
const answer = await callback(request);
return answer;
}
main.js
const inquirer = require("inquirer");
const getAnswers = require("./getAnswers");
const main = async () => {
const request = "abc";
const result = await getAnswers(request, inquirer.prompt);
...
}
And then test file will look like that:
test.js
const getAnswers = require("./getAnswers");
test("async code works", async () => {
//Arrange
const mock = async () => {
return "Correct Answer";
};
//Act
const result = await getAnswers("abc", mock);
//Assert
expect(result).toEqual("Correct Answer";);
});
Will be very grateful if someone could suggest if there is a way of testing async function without passing it as a callback?
And if the approach itself is correct.
You can use jest.mock to mock the imported dependencies rather than pass them as parameters. Here is the unit test solution:
getAnswers.js:
const inquirer = require('inquirer');
const getAnswers = async (request) => {
const answer = await inquirer.prompt(request);
return answer;
};
module.exports = getAnswers;
getAnswers.test.js:
const getAnswers = require('./getAnswers');
const inquirer = require('inquirer');
jest.mock('inquirer', () => {
return { prompt: jest.fn() };
});
describe('59495121', () => {
afterEach(() => {
jest.resetAllMocks();
});
it('should pass', async () => {
inquirer.prompt.mockResolvedValueOnce('Correct Answer');
const actual = await getAnswers('abc');
expect(actual).toBe('Correct Answer');
});
});
Unit test result with 100% coverage:
PASS src/stackoverflow/59495121/getAnswers.test.js (10.172s)
59495121
✓ should pass (6ms)
---------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
---------------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 100 | 100 | |
getAnswers.js | 100 | 100 | 100 | 100 | |
---------------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 11.367s
Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/59495121
I'm trying to test whether an async function (fire and forget) gets called.
Content.js
export async function fireAndForgetFunction() {
...
}
export async function getData() {
...
fireAndForgetFunction()
return true;
}
I would like to test if fireAndForgetFunction has been called more than once.
Current test
import * as ContentFetch from '../Content';
const { getData } = ContentFetch;
const { fireAndForgetFunction } = ContentFetch;
it('test',async () => {
const spy = jest.spyOn(ContentFetch, 'fireAndForgetFunction');
await getData();
expect(spy).toHaveBeenCalled();
})
The test result to an error saying
Expected number of calls: >= 1
Received number of calls: 0
How could I do this test?
If you don't want to await for fireAndForgetFunction in getData(), which I assume is the case, then providing a mock implementation of fireAndForgetFunction when creating the spy is your best option:
it('test', (done) => {
const spy = jest.spyOn(ContentFetch, 'fireAndForgetFunction')
.mockImplementation(() => {
expect(spy).toHaveBeenCalled();
done();
})
getData();
})
I am trying to test my node app.js script in which I have an asynchronous request sendMessageRequest () to a function sendSmtpMessage() [ a Promise ]
app.js
const sendSmtpMessage = require("./sendSmtpMessage.js");
const keys = {....};
const mailOptions = {...}
const sendMessageRequest = async () => {
try {
const result = await sendSmtpMessage(keys,mailOptions);
console.log("... SEND MSG REQUEST FULLFILLED: ", result);
} catch(err){
console.log("... SEND MSG REQUEST FAILED: ");
}
};
sendMessageRequest();
I wrote the following app.spec.js, according to doc on Testing asynchronous code ( with async/await); but I guess my sendSmtpMessage() mocking is wrong...
app.spec.js
jest.mock("../sendSmtpMessage.js");
const sendSmtpMessage = require("../sendSmtpMessage.js");
const app = require("../app.js");
// sendSmtpMessage is a mock function
sendSmtpMessage.mockImplementation(() => {
return new Promise((resolve, reject) => {
setTimeout(() => {
(oauth2ClientMock.refreshToken !== "undefined")? resolve() : reject()
, 2000
});
})
});
describe('app', () => {
let keys, mailOptions;
beforeEach(() => {
keys = {....};
mailOptions = {....}
});
afterEach(() => {
keys = {};
mailOptions = {};
});
it("should call successfully sendMessageRequest", async () => {
// GIVEN
// WHEN
// THEN
expect.assertions(1);
await expect(sendSmtpMessage).resolves.toBe("OK");
});
it("should call unsuccessfully sendMessageRequest", async () => {
// GIVEN
// WHEN
keys.oauth.refresh_token = null;
// THEN
expect.assertions(1);
await expect(sendSmtpMessage).rejects.toBeTruthy();
});
});
As the console.log output is showing errors on both expectations in each test ( on resolve and reject )
console.log
jest --detectOpenHandles --coverage "app.spec.js"
FAIL test/app.spec.js
app
✕ should call successfully sendMessageRequest (15ms)
✕ should call unsuccessfully sendMessageRequest (2ms)
● app › should call successfully sendMessageRequest
expect(received).resolves.toBe()
received value must be a Promise.
Received:
function: [Function mockConstructor]
52 | // THEN
53 | expect.assertions(1);
> 54 | await expect(sendSmtpMessage).resolves.toBe("OK");
| ^
55 | });
56 |
57 | it("should call unsuccessfully sendMessageRequest", async () => {
at Object.toBe (node_modules/expect/build/index.js:158:13)
at Object.toBe (test/app.spec.js:54:44)
● app › should call successfully sendMessageRequest
expect.assertions(1)
Expected one assertion to be called but received zero assertion calls.
51 | // WHEN
52 | // THEN
> 53 | expect.assertions(1);
| ^
54 | await expect(sendSmtpMessage).resolves.toBe("OK");
55 | });
56 |
at Object.assertions (test/app.spec.js:53:12)
● app › should call unsuccessfully sendMessageRequest
expect(received).rejects.toBeTruthy()
received value must be a Promise.
Received:
function: [Function mockConstructor]
61 | // THEN
62 | expect.assertions(1);
> 63 | await expect(sendSmtpMessage).rejects.toBeTruthy();
| ^
64 | });
65 |
66 | });
at Object.toBeTruthy (node_modules/expect/build/index.js:203:13)
at Object.toBeTruthy (test/app.spec.js:63:43)
● app › should call unsuccessfully sendMessageRequest
expect.assertions(1)
Expected one assertion to be called but received zero assertion calls.
60 | keys.oauth.refresh_token = null;
61 | // THEN
> 62 | expect.assertions(1);
| ^
63 | await expect(sendSmtpMessage).rejects.toBeTruthy();
64 | });
65 |
at Object.assertions (test/app.spec.js:62:12)
Where am I wrong ? I don't understand very well the testing process of such plain js scripts... ( use to work with vue.js, test-utils ...)
thanks for feedback and eventually on any link to make me understanding the test unit in such case...
you are not awaiting the sendMessageRequest method call itself
const sendSmtpMessage = require("./sendSmtpMessage.js");
const keys = {....};
const mailOptions = {...}
const sendMessageRequest = async () => {
try {
const result = await sendSmtpMessage(keys,mailOptions);
console.log("... SEND MSG REQUEST FULLFILLED: ", result);
} catch(err){
console.log("... SEND MSG REQUEST FAILED: ");
}
};
(async function() {
await sendMessageRequest();
})();