I'm a big fan of async/await in javascript. After all, I've seen callback hell and it's not pretty. So naturally, when I started writing my tests there was a lot of async code that needed testing and I came across some issues that I had to figure out for my tests to work properly.
In this post I'll share some real examples that'll help you test your async javascript code using Jest.
Here's how a test suite for async code should look like:
describe('scope ',()=>{it('works with async',async()=>{/* Some async code testing. */});});
Notice that the function inside describe is not async, but the one in it is.
Seed some data to test
Sometimes we need to seed our test database to have some data to work with. I'll show you two ways to achieve this:
a. Add the data you require inside each test
Check the following code:
it('should retrieve the correct product if id matches',async()=>{// Seed.constcreatedIphone=awaitproductModel.create(productIphone);// testconstfoundProduct=awaitproductService.getById(createdIphone.id);expect(foundProduct.id).toBe(createdIphone.id);expect(foundProduct.name).toBe(productIphone.name);});
The data is seeded at the beginning of the test and used later. This method is useful when we only need this particular data for this particular test. If you find yourself copy-pasting that first line in other test, consider the following method.
b. Seed the data using beforeEach
Instead of adding the data in every test, simply add it inside the beforeEach() method like so:
beforeEach(async()=>awaitcreateProducts());afterEach(async()=>awaitdbHandler.clearDatabase());describe('product ',()=>{it('test that needs data',async()=>{});it('another test that needs data',async()=>{});});
This way the products will be added before each test, and removed after each test, ensuring that each test has a clean start.
await first and expect later
Since we're using async we can await the results of our functions and then use expect to verify the results, like so:
it('should retrieve the correct product if id matches',async()=>{constfoundProduct=awaitproductService.getById(productIphoneId);expect(foundProduct.id).toBe(productIphoneId);expect(foundProduct.name).toBe(productIphone.name);});
Use resolves to await the result
Another way of testing the results of an async function is with resolves which will result in Jest waiting for the async function to finish executing.
In the following example, we wait for getById to resolve and then we check if the result is null:
it('should return null if nothing is found',async()=>{// mongoose.Types.ObjectId() generates a new ID that won't exist in the current database.awaitexpect(productService.getById(mongoose.Types.ObjectId())).resolves.toBeNull();});
Test error handling
Test that a function doesn't throw an error
We can expect for an async function not to throw an error, like so:
it('can be created correctly',async()=>{expect(async()=>awaitproductService.create(productComplete)).not.toThrow();});
Test that a function throws the correct error
We can use rejects to wait for an async function to resolve with error, and then combine it with toThrow to make sure the error thrown is the one we expect.
it('requires name and price',async()=>{awaitexpect(productService.create(productMissingName)).rejects.toThrow(mongoose.Error.ValidationError);awaitexpect(productService.create(productMissingPrice)).rejects.toThrow(mongoose.Error.ValidationError);});
Try it out yourself
Here's a Github repo where I put together all of the examples included in this article:
(MongoDB is not required because it'll run in memory, handled by the package mongodb-memory-server).
Try it out
1. Install dependencies
npm install
2. Run tests
npm test
Contribute
Feel free to contribute to this project either by leaving your comments and suggestions in the Issues section or creating a PR. More and diverse test examples are always useful. Make sure to take a look at Jest docs and the existent examples to avoid repeating.