-
If we have a part of code interacting with DB using APIs.
-
We can mock the part of testing DB calls using Jest, Vitest or Supertest Library. This can be done either:
-
Manually: By seperating the actual server listening code and main logic and testing the main logic using Postman.
-
Automatic: By using Supertest Library, we were able to mock the DB and network calls and test the main logic.
-
- Integration Testing is a type of testing where we test the interaction between two or more modules or components.
- Also, If database is not mocked out, it is an Integration Test.
- Good for use cases where we want to test the interaction between different components.
| Unit Tests | Integration Tests |
|---|---|
| Test a single unit of code. | Test multiple units of code. |
| Mock out any external calls. | Doesn't mock any external calls. |
| Use case: Test the code in isolation. | Use case: Test the code in a real environment. |
| Faster to execute. | Slower to execute. |
| Less complex. (doesn't need to start any service) | More complex. (especially for starting any services) |
| Local Development setup is not required. | Local Development setup may be required. |
Before, we start the Integration Testing, we need to write the code for:
- Bring up the external services.
- Seed data in there.
- Bring down the service when the test suite succeeds/fails
-
Initialize the express app.
-
Initialize Prisma.
-
Create Schema of Tables that we will be having in our app in
/prisma/schema.prismafile. In our case, we are usingRequesttable which contains a sample body of our request.
-
Generate the Prisma Client using following command:
npx prisma generate
-
Create two files
db.ts(Stores the Prisma Client and exports it) andindex.ts(Simple Express app with a simple/sumendpoint) insidesrcfolder.Note: We have not added the code to listen on a port, since we don't want the tests to use the 'listening code'. So, whenever the tests start -> we don't want the HTTP server to start. Thus, we store listening logic in a seperate file named
bin.ts.
-
To start the server, we will use the following command:
tsc -b && node dist/bin.jsor just use the following command:
npm run start
or
- Simply Go to the
1-intgeration-testfolder to check the code & run the above command to start the server.
Setting up the Database:
-
Install Docker and run the Postgres using Docker locally using the below command:
docker run --name 100x-postgres -e POSTGRES_PASSWORD=mysecretpassword -p 5432:5432 -d postgres
-
Inside the
.envfile created for you, replace theDATABASE_URLwith the below code:DATABASE_URL="postgresql://postgres:mysecretpassword@localhost:5432/postgres?schema=public" -
Migrate the database using the below command:
npx prisma migrate dev --name init
-
Generate the Prisma Client again using the below command:
npx prisma generate
-
Try to send a POST request to the
/sumendpoint using Postman: You should get the response as3.
-
Check the DB and ensure data is going in, through Prisma Studio:
npx prisma studio
-
Install
Vitestlibrary using below command:npm i vitest
-
We use a
docker-composeto make it easier for us to start multiple services at once. Create adocker-compose.ymlfile in the root directory and add the below code:version: '3.8' services: db: image: postgres restart: always environment: - POSTGRES_USER=test-postgres - POSTGRES_PASSWORD=mysecretpassword ports: - '5432:5432'
Note: This makes sure we don't use the production database but use a seperate database everytime we run the tests.
-
Now, comes the important part! We create a sript to do all the work we just did above and runnning this script should do all the work (from starting the services -> migrating databases -> running the tests -> getting results -> resetting db):
-
Create a new script file inside
scriptsnamedrun-integration.sh.
-
We create another script file named
wait-for-it.shwhich waits for the services to start before running the tests. (This script is taken from here).Note: On a mac, you need to install
brew install coreutils && alias timeout=gtimeoutto run the above script.
-
If you get permissions denied error: Try giving the file permissions to all the files inside the scripts folder using
chmod +x ./scripts/*.
-
Update the
package.jsonto run the script using the below command:"scripts": { "test": "vitest", "test:integration": "./scripts/run-integration.sh" }
-
-
We can also have a
Reset-DBfile to reset the DB before running the tests. Create areset-db.tsfile inside thesrc/tests/helpersfolder and add the below code:import { PrismaClient } from '@prisma/client'; const prisma = new PrismaClient(); export default async () => { await prisma.$transaction([prisma.request.deleteMany()]); };
-
Install the
supertestlibrary using below command:npm i -D supertest @types/supertest
-
Create a test file named
sum.test.tsinsidesrc/tests/folder and add the below code:import { describe, expect, it } from 'vitest'; import { app } from '..'; import request from 'supertest'; describe('POST /sum', () => { it('should sum add 2 numbers', async () => { const { status, body } = await request(app).post('/sum').send({ a: 1, b: 2, }); expect(status).toBe(200); expect(body).toEqual({ answer: 3, id: expect.any(Number) }); }); });
-
Try running the test (Make sure your prod DB is running):
npm run test:integration
-
If we want to reset the database before running each tests/describe block, we can use the
beforeEachhook in Vitest. -
If we want certain code (in our case, its resetting the DB) to run before all tests (but not before every individual tests), we can use the
beforeAllhook in Vitest.
-
Create a
.github/workflows/test.ymlwhich will run the tests on every push or PR to the main branch on github. -
Put the below code in the
test.ymlfile:name: CI/CD Pipeline on: push: branches: - main pull_request: branches: - main jobs: test: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v2 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 - name: Set up Docker Compose uses: docker/setup-qemu-action@v2 - name: Ensure Docker Compose is available run: docker-compose version - name: Copy .env.example to .env run: cp ./1-integration-test/.env.example ./1-integration-test/.env - name: Run integration script # The main script file which sets up the services & tests run: cd 1-integration-test && npm run test:integration
-
Now, everytime there's a push or PR to the main branch, the tests will run automatically and show the results like shown below π
-
End-to-End Testing is a type of testing where we test the entire application from start to end.
-
In this type of testing, we test both the frontend + backend.
-
It lets us open the browser, interact with the app and test the app as a user would.
-
Eg: Cypress, Playwright and NightwatchJS.
Drawbacks:
-
It is slow.
-
It is flaky.
-
Initialize a seperate project.
-
Install Cypress using below command:
npm install cypress --save-dev
-
Bootstrap Cypress.
npx cypress open
A new app should open. Go through the setup and choose E2E Testing -> Choose your browser -> Click on Scaffold Example Tests.
-
Remove the
cypress/e2e/2-advanced-examplesfolder as they contain extra example test files which we don't need.
-
Rename the file inside
1-getting-startedtocms.cy.jsand check the test file to see the working of Cypress testing.
-
Run the test using below command:
npx cypress run --browser chrome --headed
or just use the below command:
npm run test:e2e
Explaining the above command
npx cypress run: Runs the cypress tests.--browser chrome: Runs the tests on chrome browser. Totally Optional.--headed: Runs the tests in a headed mode (i.e. opens the browser and runs the tests). Other option can be--headlesswhich runs the tests in a headless mode (i.e. doesn't open the browser and runs the tests in the background).

