How To Write An Express JS Server Using Test Driven Development
Part One: Setting Up Environments
Why TDD In First Place?
So you have probably been in this situation before, you write software. It is working beautifully — but just hours before that big presentation. You decide to make a few changes to a few methods. You run the application, still works — or so you think.
It is time for that presentation, you get through the initial parts of the application well, until it’s time to show off the upload feature you just implemented — then boom! It is not working anymore. You try again — boom nothing, you start panicking at this point. You begin to dread being a programmer, you probably tell your boss or professor that it was working, you even swear on it.
This is where Test Driven Development comes to the rescue. It is the practice of writing tests that test that your code is doing what is supposed to do. In this practice, you write tests before you wrote the actual code. For example, if you have a function that calculates the sum of two number and returns it, you will write a test that checks that the returned sum is the expected one.
Now you probably wondering how can we test a function that is not written yet and won’t that test fail? Yes, it is supposed to initially fail. Your test has to fail until you write the function that returns the correct sum when two numbers are provided. The benefit is that those test will always check to see that in case you make some changes to your code, you have an intermediate way of knowing whether those changes haven’t broken something. For more reading on the topic here a brilliant article from :
What are we building?
Now in this tutorial am going to show you how you can build and test your Express JS Server using the Test Driven Development approach.
We are going to build a small server that provides APIs for most popular movies, searching for movies and some other CRUD functions.
What do we need?
- Express JS — Most popular Node JS Server framework.
- Config— Helps setting up environments (development, test, and production).
- Debug — Only show logs in test or development environments.
- Mongoose — An elegant ODM for MongoDB in Node JS.
- Mocha — Testing framework.
- Nodemon — Restarts our development server automatically.
- Chai — Our TDD/BDD assertion library.
- Babel CLI — A Javascript compiler.
- SuperTest — HTTP server testing library.
- Yarn — Fast, Reliable, & Secure Dependency Manager ( you are free to use npm instead, just replace the commands with npm equivalent)
Setting Up Stuff
1. mkdir movie_server ; cd movie_server2. yarn init3. yarn add mocha chai debug supertest nodemon babel-cli babel-preset-node8 config --dev4. yarn add express mongoose 5. mkdir dist //when you run yarn build,prod files will be stored here 6. mkdir tests // mocha will look for tests here.
7. mkdir src ; cd src ; touch index.js &
After you run these commands you will then go to your package.json and set up your scripts like this:
"build": "babel src -d dist --source-maps"
So the first script setups up what happens when you run yarn run build. Here we are using babel to compile all the code in the src folder and convert it to ES5 syntax and store it in the dist folder. In our production setup, our application will be running from this folder.
"serve":"NODE_ENV=production node dist/index.js",
The second script line starts the server in the production environment or mode by setting the environmental variable NODE_ENV to production. This is important because they are certain lines of code that you only want to run in production or there are certain configurations that you only want to make available in production mode. This should only be run after running yarn run build. Notice that we are not using Babel just node, this is because the code is already compiled to a version that node without any special compilers like babel using the build command.
"dev":"DEBUG=server:debug NODE_ENV=development nodemon src/index.js --exec babel-node"
This line will start the application in development mode and here we are using nodemon to make sure that whenever we change our code, the node server is able to restart and load the new changes. We also set up an environmental variable called DEBUG which we set to server:debug, this is need by the package Debug to determine whether to show console logs or not depending on the environment. Here we only want to show logs when we are running in the application in debug mode. I will explain how we do this as we go on. We also set the script to execute using babel-node, this allows us to use the latest javascript syntax ES6+.
"test":"DEBUG=server:debug NODE_ENV=test mocha --require babel-core/register --reporter spec --exit tests/ "
Lastly, we set up the script for running tests. Here our environment is set to test, we also set it to run our tests using Mocha. We make sure that we instruct mocha to babel-core/register, this is important for our ES6+ code syntax to run, without it, mocha will fail to do so. Also, we set up the reporter flag to use spec which defines how our tests will look like in our console. We also use the exit flag which commands mocha to stop its process when all the tests are done else it will hang. Last we tell Mocha to look for tests in the tests folder.
Let’s Setup Our Environments Using Config
Remember that we have three environments our application will be running in and that each will have its own variables, let’s set up each one of them which will be used by the package config. This again is important because it helps to make sure that we configure our server to use certain settings depending on the environment. For example, we can configure our server to use the development database when we run our server in development and the production database when in production mode.
To set up our config files, create a folder called configs and create three files namely: development.json, test.json, and production.json ( it is important that you name these files the same names your NODE_ENV environmental variable is holding in each of the scripts in pacakge.json). Each file should contain a JSON Object with whatever keys/value pair you wish to use. I use the name, database, and port as my keys.
// configs/development.json
{"name": "Server Developement","database": "mongodb://localhost:27017/development","port": 3000}// configs/test.json
{"name": "Server Test","database": "mongodb://localhost:27017/test","port": 3100}// configs/production.json
{"name": "Server Production","database": "mongodb://localhost:27017/production","port": 3000}
Testing The Environments
Now let’s see that our environments are working accordingly. To do so we are going to create an express js server in the index.js with the following code.
Development Mode
In the first line, I configure the imported debug function to only show its log when the DEBUG environmental variable is equal to ‘server:debug’. Am also using config.get() to get the value of the key from the JSON files we set up in the config folder. The way config works by only loading the appropriate JSON file whose name matches the NODE_ENV’s value.
Let’s now test that our server is running and it does so in the right environment depending on the command we use to start it. Here we testing that it is running the development environment using the following command.
yarn run dev
You can see that our server is running on the appropriate port set in the development.json file in the configs folder and that is running server development mode.
Test Mode
Next, we will test that our server running is able to run in test mode. But unlike the other modes which just starts the server, here will be also be running tests using Mocha.
yarn run test
Notice that is says 0 passing. Why is that? Well, this is okay, it means that Mocha is working but just didn’t find any tests and that we are running in the test environment. So we can then write tests for Mocha to run. Our first test will just check that the server is running on the port specified in the respective JSON config file being used.
In the code above, we import expect method from chai, this method allows us to make assertions about the server. In this case, we want to test that the port the server is running on, is the same as the one used in the test.json file in the config folder. We wrap our test in describe function (takes in the name and callback function) which is just used to group similar tests. The actual test is in ‘it’ function (also takes description and callback as well). The test reads like plain english: expect server.port to equal the port number in the current environments config file.
After this rerun yarn run test and you can see that the server is running in test mode and that the test passes. If you change the port number in the app.listen() method in index.js and rerun the tests, you see it fail and you will immediately know that something is wrong.
Production Mode
The last environment we going to test that it is working is the production environment but before we run yarn run serve, we need to first run yarn run build. The tells babel to compile our code into ES5 syntax and transfer the resulting files into the dist folder.
yarn run build
After running yarn run build, you see that the dist folder has new files. These files are generated so you shouldn’t make any changes to them. If you make changes to your code, just regenerate them using the same command. At this point, you can now run yarn run serve.
yarn run serve
Notice that there is no console output showing which port the server is running on, this is because in the serve script we haven’t set DEBUG=server:debug cause we don’t want to show logs when running the application in production mode. The rule is not you should never show logs in production mode, especially error logs, as they might contain sensitive information. But since it is necessary to know which port, the server is running on, you can add this line:
console.log(`server is running on port ${config.get('port')} and in ${config.get('name')} mode`);
Stop the server using ctrl +d run yarn run build and then run yarn run serve and you will see the following message.
Conclusion
So for this part of the tutorial, we have covered what TDD is, how it can help you make changes to your code with confidence and avoid those embarrassing moments. We have also covered how to set up your project with the necessary packages and also covered how to setup up three environments for your server to run in. In the next of the tutorial, I will cover how to now write your MongoDB Schemas and server APIs using TDD with SuperTest and Chai.
The code for this tutorial can be found here: https://github.com/makayi/express-tdd-tutorial
Thanks for reading and happy coding.
UPDATE: part two is out