This article is a guest post by Ukpai Ugochi. She wrote this blog post through the Write for the Community program.
Strapi is a headless content management system (CMS) for NodeJS-based applications. Like most CMS, Strapi allows you to build NodeJS-based applications with little or no coding experience.
With Strapi, you can build fast applications with client-side like Vue.js, React, Angular, etc. or, Jamstack applications.
After building your Strapi application, you'll want to be sure there are no bugs in it and that it behaves expectedly. No one wants to ship applications to users with bugs in them, and you want your application to function as expected.
For example, you don't want to find out your application's server is down after shipping it to users.
How, then, can you make sure that your application is free from bugs and other abnormalities? This abnormality is where software testing comes in place.
Software testing is the process of evaluating software functionality to ensure its actual functionality is in line with the expected functionality. There are various methods of software testing.
In this article, we'll be exploring unit testing with Jest, Supertest, and sqlite3 and end-to-end testing with Nightwatch.js in your Strapi application.
Unit testing is a type of software testing that involves testing different modular components of the software.
This testing method aims to make sure that individual units of your application are working as expected.
In this section, we'll be exploring how to carry out unit testing in your Strapi application with Jest, Supertest, and sqlite3.
We'll be using Jest for snapshot testing while integrating Supertest to test our API routes and sqlite3 for creating an on-disk database created for tests and deleted after tests.
The reason for this is to enable us to manage our test data correctly so that test data doesn't interfere with our application's database.
To test your Strapi application, you need the following prerequisites.
Perquisites
NPX
by running the following command.1`npx create-strapi-app my-project --quickstart`
NPX
comes with installing NodeJS, and it makes it easy to use CLI tools and other executables hosted on the NPM registry.
Start your Strapi application by running the command below on your CLI.
1npm run develop
Next, we'll install and set up all the test tools necessary for unit testing our Strapi application.
Jest
Jest is a JavaScript testing library that allows you to run tests for your application. Facebook maintains Jest; it is mainly used for unit testing.
To get started with Jest, install it in your working environment by running the command below in your CLI.
1npm install jest --save -dev
After installing Jest, let's install Supertest and sqlite3.
Supertest and sqlite3
Supertest is used for testing API endpoints in HTTP requests. We'll be using Supertest together with Jest because part of the test we'll be writing involves testing our routes.
To install Supertest and sqlite3, run this command in your CLI.
1npm install supertest sqlite3 --save-dev
Set-Up
Now that we have all of our test tools installed, you'll need to set up your application. In your package.json
file, add a test
command to your scripts
section.
1"test": "jest --forceExit --detectOpenHandles"
Your scripts section should now look like this.
1 "scripts": {
2 "develop": "strapi develop",
3 "start": "strapi start",
4 "build": "strapi build",
5 "strapi": "strapi",
6 "test": "jest --forceExit --detectOpenHandles"
7 },
Next, add the Jest configuration to your package.json
file. This script should add the configuration below at the bottom of your package.json
file.
1 "jest": {
2 "testPathIgnorePatterns": [
3 "/node_modules/",
4 ".tmp",
5 ".cache"
6 ],
7 "testEnvironment": "node"
8 }
Now, let's create a configuration file for the jest test environment and configure a separate SQLite database for our test. In your config
folder, create a folder and name it env
; in env
-, create a test folder.
In your test folder, create a file and name it database.json
. database.json
should have this path ./config/env/test/database.json
. Add the codes below to your database.json
file.
1{
2 "defaultConnection": "default",
3 "connections": {
4 "default": {
5 "connector": "bookshelf",
6 "settings": {
7 "client": "sqlite",
8 "filename": ".tmp/test.db"
9 },
10 "options": {
11 "useNullAsDefault": true,
12 "pool": {
13 "min": 0,
14 "max": 1
15 }
16 }
17 }
18 }
19}
Next, we'll configure our Strapi application to create an entry point for our application. Create a folder in the root of your application, name it tests
, create a folder in tests
, and call it helpers
.
In helpers
, you'll create an entry point file for your Strapi application and name it strapi.js
. Put the codes below in Strapi.js
.
1const Strapi = require('strapi');
2const http = require('http');
3let instance;
4async function setupStrapi() {
5 if (!instance) {
6 /** the following code is copied from `./node_modules/strapi/lib/Strapi.js` */
7 await Strapi().load();
8 instance = strapi; // strapi is now global
9 await instance.app
10 .use(instance.router.routes()) // populate KOA routes
11 .use(instance.router.allowedMethods()); // populate KOA methods
12 instance.server = http.createServer(instance.app.callback());
13 }
14 return instance;
15}
16module.exports = { setupStrapi };
Now, we'll write our first jest test to test our entry point file we just created. Create a file in the tests
folder and name it app.test.js
. Now, paste the codes below in your app.test.js
file.
1const fs = require('fs');
2const { setupStrapi } = require('./helpers/strapi');
3/** this code is called once before any test */
4beforeAll(async done => {
5 await setupStrapi();
6 done();
7});
8/** this code is called after all the tests are finished */
9afterAll(async done => {
10 const dbSettings = strapi.config.get('database.connections.default.settings');
11 //This will delete test database after all tests
12 if (dbSettings && dbSettings.filename) {
13 const tmpDbFile = `${__dirname}/../${dbSettings.filename}`;
14 if (fs.existsSync(tmpDbFile)) {
15 fs.unlinkSync(tmpDbFile);
16 }
17 }
18 done();
19});
20//Run test to make sure Strapi is defined.
21it('strapi is defined', () => {
22 expect(strapi).toBeDefined();
23});
If you're using Windows OS, you'll get an error trying to delete the test database after each test. This error is because of SQLite locking in windows. So, you have to remove the block of code that'll delete the test database.
If you do that successfully, your app.test.js
should look like the program below.
1const fs = require('fs');
2const { setupStrapi } = require('./helpers/strapi');
3
4/** this code is called once before any test */
5beforeAll(async done => {
6 await setupStrapi();
7 done();
8});
9
10/** this code is called after all the tests are finished */
11afterAll(done => {
12 const dbSettings = strapi.config.get('database.connections.default.settings');
13 done();
14});
15
16it('strapi is defined', () => {
17 expect(strapi).toBeDefined();
18});
A permanent solution to avoid the sqlite3 locking in WindowsOS is to test with another database aside from sqlite3 like MariaDB, MongoDB, etc.
Also, your database.json
file in ./config/env/test/database.json
will contain only the filename if you're using windows and sticking with sqlite3.
1{
2 "filename": ".tmp/test.db"
3}
We'll be running a test from app.test.js
to make sure Strapi is defined. To run our test, execute the command below in your command-line interface.
1./node_modules/.bin/jest
Now, we'll test the API endpoints of our Strapi application with Supertest
. Create a folder in your tests
folder and name it hello
.
Now, in your hello
folder, create a file and name it index.js
. In your index.js
file, put in the following codes.
1const request = require('supertest');
2it('should return This is my first blog', async done => {
3 await request(strapi.server) // app server is an instance of Class: http.Server
4 .get('/blog')
5 .expect(200) // Expect response http code 200
6 .then(data => {
7 expect(data.text).toBe('This is my first blog'); // expect the response text
8 });
9 done();
10});
Next, we'll be creating our blog file with the blog
endpoint that we'll be testing. Create file routes.js
in ./api/blog/config/routes.json
. In your routes.js
file, put in the following codes.
1{
2 "routes": [
3 {
4 "method": "GET",
5 "path": "/blog",
6 "handler": "Blog.index",
7 "config": {
8 "policies": []
9 }
10 }
11 ]
12}
Next, we'll create our blog
file in ./api/blog/controllers/blog.js
. In your blog file, put in the following codes.
1module.exports = {
2 // GET /hello
3 async index(ctx) {
4 ctx.send('This is my first blog');
5 },
6 };
In your ./tests/app.test.js
file, add the following code to reference your index.js
file.
1require('./hello');
Start your Strapi server with strapi start
in your CLI and navigate to http://localhost:1337/admin/settings/users-permissions/roles in your browser.
Click on the public
under the list of roles, tick select all
in the blog under permissions, and click save
.
You can navigate to http://localhost:1337/blog](http://localhost:1337/blog
in your web browser, and you'll see the text. This is my first blog now. Run your code with the command below in your CLI.
1./node_modules/.bin/jest
There are so many other tests that you can perform on your Strapi application with Jest and Supertest. For instance, you can test authorization endpoints and also perform continuous integration with a continuous integration environment.
In the next section, we'll be exploring end-to-end testing with Strapi and nightwatch.js.
End-to-end testing is a type of software testing that involves testing the overall flow of the software. This testing method is not like the unit test method where individual units of the software are tested. Instead, the software is tested from start to end and it’s integration with external interfaces.
The purpose of end-to-end test is to make sure communication with subsystems, interfaces, and databases and software dependencies are all working simultaneously as expected. In this section, we will look at how to implement end-to-end tests in Strapi with Nightwatch.js.
Nightwatch.js and Strapi
Nightwatch.js is an open-source software testing framework built in Node.js that allows developers to perform end-to-end testing. It uses the powerful W3C WebDriver API to execute commands and assertions.
Let's install and set up nightwatch.js:
Set-up Nightwatch.js
Remember how I mentioned that Nightwatch.js uses the powerful W3C WebDriver API to perform commands and assertions? Nightwatch.js communicates over an HTTP API with a WebDriver server like chrome driver or selenium driver.
In older versions of Nightwatch.js, you'll need to install a selenium stand-alone after installing the chrome driver.
However, for Nightwatch version 1.1 and above, you don't need to install selenium-standalone except you're testing against legacy browsers like internet explorer. I'll be using the terminal in VSCode to run my codes; however, you can use any CLI of your choice.
Open your VSCode terminal or navigate to the path of the Strapi project you just created on your CLI. Run the code below to install nightwatch.js and chrome driver.
1npm install nightwatch chromedriver
Now, you have nightwatch.js and chrome driver installed locally. Open your package.json file, and you'll see the latest version of nightwatch.js and chrome driver installed.
Next, we'll create a base integration file for nightwatch.js. This file will contain our server path, the port our chrome driver should use, and other basic configurations. Create a file in the root of your project and name it nightwatch.json
.
1/my-project
2└─── api
3└─── build
4└─── config
5└─── extensions
6└─── public
7- favicon.ico
8- package.json
9- nightwatch.json
In nightwatch.json
, copy and paste the following codes below. I'll be explaining them immediately.
1{
2 "src_folders" : ["tests"],
3 "webdriver" : {
4 "start_process" : true,
5 "server_path": "node_modules/chromedriver/lib/chromedriver/chromedriver.exe",
6 "port" : 9515
7 },
8 "test_settings" : {
9 "default" : {
10 "desiredCapabilities" : {
11 "browserName" : "chrome"
12 }
13 }
14 }
15}
The key src_folders
indicate the folder where tests will be reading, while the value ["tests"]
represents the folder's name.
The key and value "start_process": true
indicates that you want nightwatch.js to start the web driver during tests, while "server_path"
: "node_modules/chromedriver/lib/chromedriver/chromedriver.exe"
and "port": 9515
indicates the web driver path and port it'll be using for the test.
Lastly, we have "test_settings,"
which places Chrome as the default browser for tests "browserName": "chrome."
Now that we understand the function of the codes in our nightwatch.json
file, we'll write an end-to-end test.
The first test we'll be writing is to make sure our Strapi server is running fine in our local environment. We want to make sure the path http://localhost:1337/admin
is free and running fine. Firstly, we'll create a folder and name it tests
.
Next, we'll create a JavaScript file where we'll write our tests and name it test
. In your test file, please copy the code below into it. Let's look at the codes we have written for our test.
1module.exports = {
2 'server test': (server) => {
3 server.url('http://localhost:1337/admin')
4 .waitForElementVisible('body', 10000)
5 .assert.title('Register-admin')
6 server.expect.element('button').text.to.equal('English')
7 server.expect.element('label').text.to.equal('First name')
8 },
9};
What we've done above is to create a server test for our Strapi application. We have set our server URL to http://localhost:1337/admin](http://localhost:1337/admin
. Because you've not been authenticated, Strapi will redirect you to http://localhost:1337/admin/auth/register-admin
.
Next, we'll tell our driver to wait for the body of our application to be visible, setting a timeout to 10,000 ms. This is our first test.
Our second test will check that Strapi redirects us to the registration page by confirming that our page title is Register-admin
.
Next, we'll write two more tests to be sure we're on the registration page. We'll tell the server to expect a button with the text English
and a form label with the text Firstname
.
You can execute this program by running ./node_modules/.bin/Nightwatch
in your VSCode terminal. Because all of the conditions are met, i.e., there's a button with the text English
, etc.
You’ll have OK. 4 assertions passed.
as your output.
The diagram below is our chrome driver running on port 9515
, accessing our server URL, which is http://localhost:1337/admin](http://localhost:1337/admin
.
Now, we'll register with our first name and other details to get authenticated. I set my email as hanny@mail.com and password as Mydear100
. This information will be helpful when writing tests. You can set your email and password to whatever you like. Remember to change that in the test we'll be writing about soon.
Fill in the other information and click on the Let``'``s Start
button. You’ll be directed to the admin panel.
We'll write a test that'll make sure when we input our correct login details, and it'll direct us to the admin panel. The first thing I'll like you to do is look at the page's title and redirect you to after authentication and ensure it is Homepage.
Next, replace the content of your test.js
file with the program below.
1module.exports = {
2 'Server test': (server) => {
3 server.url('http://localhost:1337/admin/auth/login')
4 .waitForElementVisible('body', 10000)
5 .assert.title('Login')
6 .setValue('input[name="email"]', 'hanny@mail.com')
7 .setValue('input[name="password"]', 'Mydear100')
8 .click('button[type="submit"]')
9 server.assert.title('Homepage')
10 },
11};
The program above will navigate to http://localhost:1337/admin/auth/login
, wait for the body of your page to become visible, and make sure the page title is Login
. Next, it’ll put in hanny@mail.com
as your email and Mydear100
as your password and click the submit button.
Finally, it’ll make sure the title of the page you’re currently on is Homepage
.
When you run this test by executing ./node_modules/.bin/nightwatch
in your vscode terminal, you’ll get OK. 3 assertions passed.
Your chrome driver will navigate to http://localhost:1337/admin/auth/login
, showing your password and email.
Next, it’ll click on the login button, taking you to the admin panel.
One thing I'll like you to know is how the assert
method behaves. When running your tests, if a failed assertion is recorded, the test will come to a stop even though other assertions could have been successful.
This is because assertions test assumptions about your program and are always required to come out true
.
For example, the block of code below won't input an email or password and would throw an error because the title page is assumed to be Register
when it ought to be Login
. Even though the next title page should have been Homepage, this code block will throw an error and bring the test to an end.
1module.exports = {
2 'Server test': (server) => {
3 server.url('http://localhost:1337/admin/auth/login')
4 .waitForElementVisible('body', 10000)
5 .assert.title('Register')
6 .setValue('input[name="email"]', 'hanny@mail.com')
7 .setValue('input[name="password"]', 'Mydear100')
8 .click('button[type="submit"]')
9 server.assert.title('Homepage')
10 },
11};
Testing is an essential part of software development. There's a branch of quality assurance in software development called test automation.
The job of a test automation engineer is to write programs that run automated tests on new or existing software.
You must hire the service of a test automation engineer or any other test engineer in your firm. This is to enable them to test that the functionality of your product is working as expected.
This article explored how to carry out a unit and end-to-end tests in our Strapi application.
First, we discussed unit tests and how to carry out unit tests in our Strapi application with Jest, Supertest, and sqlite3.
Next, we discussed end-to-end tests and how to test some of our application functionalities with nightwatch.js.
There are so many tests you can write with nightwatch.js for your Strapi application. Also, nightwatch.js comes with numerous API methods aside from those discussed in this article.
Ukpai is a full-stack JavaScript developer and loves to share knowledge about her transition from marine engineering to software development to encourage people who love software development and don't know where to begin.