Table Of Contents
Testing one of the most important skills you can known as a developer, It's something a lot of people don't teach or focus on but if you known testing, So I had to try and figure it out here in part(2). you can read more in article Testing part(1) in Node JS with Express using Mocha and Chai
About Testing!
Unit Testing
Tests a single fully isolated unity of the application.
Integration Testing
Tests the interaction of a unit along with its dependencies. e.g. A function that calls another function meaning that the test results are also depending on the function being called within the parent function.
End-2-End Testing
Full flow tests. From the front-end button click to the API endpoint consuming.
Jest and SuperTest Introduction
About Jest
- Jest is a wonderful testing library created by Facebook to help test JavaScript code, Express APIs, React components, and much more.
- What's great about Jest is it not only has a similar syntax to other testing/assertion libraries like Jasmine and Chai.
- And with Jest your tests run in parallel so they are executed much faster than other testing frameworks.
About SuperTest
SuperTest is an HTTP assertions library that allows you to test your Node.js HTTP servers.
It is built on top of SuperAgent library, which is an HTTP client for Node.js.
Who apply testing ?!
(1) Getting Started
So, in this article we're going to use the blog REST API I built using Express and Mongoose
which you can fetch Github repo here
- To use jest globally we can install it with
$ npm install -g --save-dev jest supertest
- Once the packages are installed, we need to setup a test command in our
package.json file
{
// ...
"scripts": {
"test": "jest"
}
// ...
}
(2) Test preparation
- Writing your first test (You can make the test pass by expecting 1 === 1). ```javascript
// This passes because 1 === 1
it('Testing to see if Jest works', () => {
expect(1).toBe(1)
})
**(3) Testing Endpoints**
- Before you can test endpoints, you need to setup the server so Supertest can use it in your tests.
- Most tutorials teach you to listen to the Express app in the server file, like this:
```javascript
const express = require('express')
const app = express()
// Middlewares...
// Routes...
app.listen(3000)
- This doesnโt work because it starts listening to one port. If you try to write many test files, you will get an error port in use.
- So you want to allow each test file to start a server on their own. To do this, you need to export app without listening to it.
const express = require('express')
const app = express()
// Middlewares...
// Routes...
module.exports = app
- For development or production purposes, you can listen to your app like normal in a different file like index.js. ```javascript
const app = require("./server");
app.listen(5000, () => {
console.log("Server has started!");
});
**(4) Using Supertest**
- To use Supertest, you require your app and supertest in the test file.
```javascript
const app = require("../server");
const mongoose = require("mongoose");
const supertest = require("supertest");
beforeEach((done) => {
mongoose.connect("mongodb://localhost:27017/JestDB",
{ useNewUrlParser: true, useUnifiedTopology: true },
() => done());
});
afterEach((done) => {
mongoose.connection.db.dropDatabase(() => {
mongoose.connection.close(() => done())
});
});
- Those are functions that will be invoked before and after every single test cases. This allows us to connect to MongoDB and remove all the data once a test case is finished
-
In Jest, these are done using four different functions:
- beforeAll - called once before all tests.
- beforeEach - called before each of these tests (before every test function).
- afterEach - called after each of these tests (after every test function).
- afterAll - called once after all tests.
(5) Using routes
- We also want to initialize our Express server in app variable which will be accessible from our test case. Let's create a new test case called
GET /api/posts
. ```javascript
test("GET /api/posts", async () => {
const post = await Post.create({ title: "Post 1", content: "Lorem ipsum" });
await supertest(app).get("/api/posts")
.expect(200)
.then((response) => {
// Check type and length
expect(Array.isArray(response.body)).toBeTruthy();
expect(response.body.length).toEqual(1);
// Check data
expect(response.body[0]._id).toBe(post.id);
expect(response.body[0].title).toBe(post.title);
expect(response.body[0].content).toBe(post.content);
});
});
* Here, we're adding a new document to our database so that we won't get an empty response. Then, we send a GET request using SuperTest to the /api/posts
endpoint and expect the response status to be 200
- which means success. Finally, we check if the response matches the data in the database.
We can now run our tests by running npm test
Matchers
Jest has quite a few functions used for assertions/expectations. You can see a full list here, but these are some common ones.
- toBeDefined
- toBeGreaterThan / toBeLessThan
-
toBe (uses === to compare)
-
toEqual (for deep object comparison)
-
toContain (see if a value is inside of a collection)
Now, let's test the get single post .
test("GET /api/posts/:id", async () => {
const post = await Post.create({ title: "Post 1", content: "Lorem ipsum" });
await supertest(app).get("/api/posts/" + post.id)
.expect(200)
.then((response) => {
expect(response.body._id).toBe(post.id);
expect(response.body.title).toBe(post.title);
expect(response.body.content).toBe(post.content);
});
});
</code></pre></div><h3>
<a name="we-can-now-run-our-tests-again-by-running-raw-npm-test-endraw-" href="#we-can-now-run-our-tests-again-by-running-raw-npm-test-endraw-">
</a>
We can now run our tests again by running <code>npm test</code>
</h3>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/i/1mvul58a5uawa4esdljw.PNG" alt="Alt Text"></p>
<h1>
<a name="conclusion" href="#conclusion">
</a>
Conclusion! <a name="chapter-4"></a>
</h1>
<p>I played around with testing lately. One thing I tried to do was to test the endpoints of my Express application.</p>
<p>If you enjoyed this article, please tell a friend about it!<br>
Share it on Twitter. If you spot a typo, Iโd appreciate if you can correct it on <a href="https://github.com/mohamedlotfe/unit-testing-api-nodejs-jest">GitHub</a>.<br>
You can contact me through:</p>
<p>Gmail: <a href="mailto:mhmdlotfy9@gmail.com">mhmdlotfy9@gmail.com</a><br>
or <a href="https://www.linkedin.com/in/mohamedlotfybasher/">Linkedin</a><br>
Thank you</p>
Top comments (8)
I've started playing with this and do tests run in the order I write them and can/should I rely on the actions of the previous one. For example I need to test whether I can add a post, and whether I can recieve a post, rather than doing what you did (at "Now, let's test the get single post"). Is it bad practice to write the something like this
Hey Aural
firstly, this is a a base article a bout how to use unit test.
and second one that what you did is just the post (or data) variable shared for creating and fetching steps not a big deal so far.
and last thing is to Mock your data "preparing step" then use it whatever you need
check this lib npmjs.com/package/given2
Thanks
another great article
teslam edak ya sadeky
thanks
very helpful
thank you
great article, thanks.