Consider the code snippet below for creating fake accounts:
export const accounts = [];
export const emails = [];
export async function createAccount(name, email) {
accounts.push({name, email});
void sendEmail(email);
}
async function sendEmail(email) {
setTimeout(0, () => emails.push(`Account created for ${email}`));
}
The createAccount()
function saves an account in the array and also sends an email reporting the account creation. It is important to note that the createAccount()
function does not wait for the email to be sent as it may take some time to complete.
However, when we attempt to test the email sending functionality:
import {assert} from 'chai';
import {createAccount, emails} from './index.js';
describe('test', async () => {
it('test', async() => {
await createAccount('John Doe', 'johnDoe@example.com');
assert(emails[0], 'Account created for johnDoe@example.com');
});
});
Unfortunately, the test does not pass as expected...
$ npm run test
> test
> mocha test.js
test
1) test
0 passing (5ms)
1 failing
1) test
test:
AssertionError: Account created for johnDoe@example.com
at Context.<anonymous> (file:///home/me/sandbox/foo/test.js:7:5)
The reason the test fails is because the sendEmail()
function is called asynchronously and may not have sent the email yet.
How can we effectively test if the email was eventually sent without:
- revealing the return value of
sendEmail()
; - relying on delays or timeouts.
Any suggestions on how to achieve this?
Appendix
To simplify testing, you can use the following package.json
:
{
"name": "foo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "mocha test.js"
},
"type": "module",
"author": "",
"license": "ISC",
"devDependencies": {
"chai": "^5.1.1",
"mocha": "^10.6.0"
}
}