Testing NodeJS Express App
What is an integration test?
“ Integration tests determine if independently developed units of software work correctly when they are connected to each other. The term has become blurred even by the diffuse standards of the software industry, so I’ve been wary of using it in my writing. In particular, many people assume integration tests are necessarily broad in scope, while they can be more effectively done with a narrower scope. “ Martin Fowler.
Testing an Express app
So as to see how we can test an Express app we need to create a simple Express application. I used MongoDB and its native driver(We usually use Mongoose as an ODM) but in order to keep it as simple as possible I didn’t use Mongoose also, I implemented users directly in app.js
which is not a good practice but it helps us to focus on how tests are working. You can take a look at this repository to see a better boilerplate for your NodeJS app.
Installing packages
express: we need express to implement our routes.
jest: Jest is our testing framework and it gives us a test environment.
supertest: we use Supertest for making requests.
mongodb: It is MongoDB native driver(For simplicity we are not using an ODM).
mongodb-memory-server: This is an in-memory mongodb database and we are going to use it to run our tests.
Create a Jest config file
Jest needs a jest.config.js
file to find our test files.
* My project express app files are inside src
folder so in line 2 I used <rootDir>/src
* In line 3 we need to enter a regex to separate test files from source files. This Regex returns true when the file has test.js
or spec.js
extensions or it is under __test__
folder(Jest recommends putting tests in this folder).
Create an Express app
This is a simple express app and the body-parser is used to handle the body of post requests.
Create a database connection
We need two kinds of the database :
- Permanent database: It can be a docker container or mongodb cloud or a service on your local machine which persists data of the application.
- Test database: Usually people use a temporary database which is fast so we can run our tests faster. We can use an in-memory version of mongodb using packages(we use this way in this example because it is easier :) ) or we can use a docker.
How do we know that we are testing?
By using environment variables. When we run tests using Jest this variable is process.env.NODE_ENV
equal to test
.
So when we are in development or production mode we can use a permanent database and in test mode, we need to use another database.
So let’s create a simple connection file :
Now we are going to scrutiny this file line by line.
getDB function(4–16): This function gets an instance of the database connection(The better practice of getting an instance of connection is using a Singular pattern you can see one way of implementing this pattern in this article).
getTestDataBaseURL function(18–25): In order to run tests we need an in-memory database and we are going to use mongodb-memory-server
npm package. This package creates a mongodb database on a random free port(you can use a static port by passing options to your instance). When we are creating an instance by calling its constructor we can use getUri
function to get the database URL. this promise returns a URL like this : mongodb://localhost:27021
module.exports(27–42): We want to use an in-memory database when we are testing so we can use a condition an check the value of process.env.NODE_ENV
.
Why we used the condition line 31? if (!URL){}
Each time we create an instance of MongodbMemoryServer
there is a new raw database and by checking the URL value I don’t want to create a new instance of MongodbMemoryServer
, the first time I create an instance I change the URL variable so next time I use that URL instead of creating a new mongodb in-memory database.
Find users and insert user
We implemented these routes inside app.js
.
Test!
Finally, we are ready to implement our tests.
An imperative point is that we need to import our express instance here.
By using supertest
package we can make requests.
this is the way we can make a request using supertest
:
const res = await request(app).get('/users');
Since the database is empty we expect users to be []
.
In the second test, we are inserting a user and we are getting users so we expect the users to be an array with one element.
In the last test, we are inserting a user and we expect the response to have properties like insertedCount
.
This was a simple example of testing an express application and this is the repository.