-
Notifications
You must be signed in to change notification settings - Fork 12
Integration and Unit testing
Integration testing is a kind of testing in which software modules are logically integrated and tested as a group. A typical software project consists of multiple software modules coded by different programmers. This level of testing aims to find bugs in the interactions of these software modules when they are integrated.
One of the main challenges we faced while adding integration testing was the interdependencies of microservices. The solution to this was running the test cases inside a docker container, this way we eliminate the dependency issues and provides a clean environment each time the test is run.
The testing framework we choose is JEST. It works out of the box and it primarily focuses on simplicity.
SuperTest was used for the HTTP assertions. It extends another library called superagent, a JavaScript HTTP client for Node.js and the browser. We can use SuperTest as a standalone library or with our testing framework Jest.
Inside the test, we have a folder for each controller. Each has 3 files. For example, the account controller has accountData.js,account.spec.js, and responseSchema.js.
.
├── account
│ ├── accountData.js
│ ├── account.spec.js
│ └── responseSchema.js
├── commonTests.js
...
Almost all of our APIs require authentications so commonTests acts like a helper for all the test cases. When called it creates and logs in with an account returning the user details including the token. Since all API calls need some common headers like X-auth-token
or Connection
, we can set these as the default header for all API calls made with superTest, this was implemented with the help of this package.
The actual test cases are in the account.spec.js file.
describe
breaks your test suite into components. Depending on your test strategy, you might have a describe for each function in your class, each module of your plugin, or each user-facing piece of functionality.
it
is where you perform individual tests. You should be able to describe each test in a little sentence, such as "/user/v1/account /create". You shouldn't be able to subdivide tests further-- if you feel like you need to, use describe instead.
describe("/user/v1/account", function () {
it("/create", async () => {});
it("/login", async () => {});
it("/verifyMentor", async () => {});
});
await request.post('/user/v1/account/login').send({
email: email,
password: password,
})
Where request
is defined as const request = defaults(supertest(baseURL)) //supertest hits the HTTP server (your app)
.
This is an example of the post method where the body is passed via the .send()
. Other HTTP methods can be used instead of .post
, headers can be set using .set
.
For more examples and options refer superagent.
Any seeding-related function should be inside the data file. It helps us to insert required data into the DB directly. This is useful since we will be running our tests in a sandboxed environment.
Validating the response of the test cases is an important aspect of integration testing. Instead of matching only the status code received, we can do deep assertion checks. Jest has expect
out of the box. It gives you access to several "matchers" that let you validate different things.
But we can go further and validate the JSON schema we received, this way we can minimize the chances of missing keys and their data type.
We use this package to validate our response schema.
JSON Schema is nothing but a contract for your JSON document that defines the expected data types and format of each field in the response.
JSON schema generator is a handy tool to generate the JSON schema.
So in responseSchema.js, we store all the expected response JSON schema which we use to validate the actual response we get from the test.
Use the command to run the integration testing
npm run test:integration
Since we're already using jest for our unit testing we can specify the config file that we want to use with integration testing. This can be done using with the help of Jest CLI Options.
jest --verbose ./integration-test --config=integrationJest.config.js --runInBand
this is the actual command hidden in npm run
Here the --verbose
helps us to display individual test results with the test suite hierarchy.
./integration-test
is the path to the test files and it looks for files ending in .spec.js or .test.js by default.
--config=integrationJest.config.js
is the path to a Jest config file specifying how to find and execute tests.
--runInBand
to run all tests serially in the current process.
In the jest config file, we can have setupFilesAfterEnv which is nothing but a list of paths to modules that run some code to configure or set up the testing framework before each test file in the suite is executed. Here we use it to connect to our database.
To run all the tests we can use CircleCi.CircleCI is a continuous integration and continuous delivery platform that can be used to implement DevOps practices.
Assuming that you've already connected GitHub with circleci, we can go into the config file located in ../.circleci/config.yml
.By default circleci uses docker containers, since we will be using docker containers to set up the server it better to use a Linux VM, this is to reduce the performance issues that we will face because of running docker inside docker.
...
build:
machine: #Linux machine instead of docker environment
image: ubuntu-2004:202111-01
docker_layer_caching: true
...
when the docker_layer_caching
is set to true circleci will try to cache the docker images, making the test get completed faster.Click here to read more. To make the test results available in circleci we can use jest-junit and add
- store_test_results:
path: ./dev-ops/report
to the circleci config file to make the test results available after the VM is torn down.
Unit testing may be a software development process in which the minor testable parts of an application, called units, are individually and independently scrutinized for correct operation. This testing methodology is completed during the development process by the software developers and sometimes QA staff. The objective of unit testing is to isolate written code to test and determine if it works as intended.