Step-by-step: Building a Node.js server (2021 edition) —Part 4/4 — Testing

Koa.js, GraphQL, MongoDB, Docker, Mocha, Typescript
Koa.js, GraphQL, MongoDB, Docker, Mocha, Typescript

If you want to use the server starter directly without going through the tutorial, find the code on Github. Link to the next parts are at the bottom of this page.

In part I to III we built a generic server with the help of Koa.js, GraphQL, MongoDB and Docker. Let’s add some tests with Mocha to make sure we don’t introduce regressions when we add more code.

Requirements: Knowing why testing is important (😉).

☕ Mocha Setup

Mocha is a javascript test runner. We will use it to test both our graphQL resolvers and database accessors, to ensure the server is responding as we expect. We also have to add an assertion library, because Mocha does not have one bundled. We will use Chai, as it’s very popular and there’s plenty tutorial/resources available for learning it.

Let’s add the following packages:

npm i -D mocha chai @types/mocha @types/chai apollo-server-testing mongodb-memory-server
  • We will use apollo-server-testing to mock a GraphQL client.
  • To mock the database without using/conflicting with the development one, we will use mongodb-memory-server. It will create a temporary MongoDB database in the RAM for our tests.

Let’s add the test command to our scripts in the package.json file:

"test": "mocha --require ts-node/register test/**/*.ts"

We need to use ts-node to transpile typescript into javascript mocha can read.

We also want to exclude the test folder from the docker copy and typescript compilation. This means adding test to the excluded folders section of tsconfig.json and to the list in .dockerignore.

Writing the tests

Mongo-memory-server is super useful to quickly set up a temporary database that will only live in the host’s RAM during the tests. Create a test/TestDatabaseManager.ts file and copy/paste the following code:

Similarly to the AppDatabaseManager we already wrote for our actual server, the TestDatabaseManager inherits from ADatabaseManager. Instead of using MongoDB’s NodeJS driver to connect to a real database, we’re using MongoMemoryServer to generate a temporary database (named “test”) and a connection string (uri).

Regarding the actual tests, we’d like to connect to the test database before running them, as well as disconnect when they are done. We’d also like to clean the database between each test. To solve this, Mocha is providing “hooks” that will allow us to run some code before and after all the tests, and even in between.

The ideal architecture would be to split tests into multiple files and still be able to benefit from the hooks and access the common variables in each one of the files:

The test architecture
The test architecture

Let’s create a file (corresponding to the red one above) called test/all_db.test.ts and add the following code:

  • Mocha is using “describe(name, suite)” to create a suite of tests. The “all” suite is encapsulating all the other suites (the “user” one at line 30, and we can add as many as we want using the importTest() function).
  • In before, we are instantiating dbManager because we will need it to access the database in the tests.
  • We are creating a test client, provided by apollo-server-testing (and relying on the TestDatabaseManager). We will use this test client to test our GraphQL API.
  • In after, we are properly cleaning the resources we used.
  • In beforeEach, we are cleaning the database. It ensures there’s no data left from the previous test when executing the tests one by one.

Let’s add test/common.ts:

It is simply a data class, holding the variables we need for our tests.

The last file we need to add is the actual user test file (test/user.test.ts), containing 2 tests:

  • From line 8 to 36, we are testing the addUser endpoint. First by testing the GraphQL mutation, and what it is returning (by comparing data and user). Then, by checking that an entry was created in the database (comparing dbUser and user).
  • From line 38 to 64, we are testing the user endpoint.
  • If you need more info about how the testClient is working, check Apollo’s official documentation. Basically we are using the query and mutate functions that createTestClient() is returning us, to mock a GraphQL client.

Now we can run npm run test and see our tests running 😎.

Output of npm run test
Output of npm run test

That’s it! We covered many topics in these articles, and we now have a generic foundation that can easily be extended to meet any need. 🥳

We won’t cover the deployment since there are already a ton of resources available out there. It should be pretty straightforward because our whole back-end is containerized. Let me know if you would like to know more about it.

Thank you for making it to the end. I hope you learned something along the way, and I would love to hear your input and feedback if some things could be done better using other techniques. Big thanks to the authors of these articles too, they were very valuable resources to build this tutorial and I recommend them to everyone:

Written by

Software developer — theopnv.com

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store