Fast recap, Integration Testing is a type of software testing where individual software modules or components are combined and tested as a group to ensure they work together seamlessly as intended. It focuses on verifying the interactions and interfaces between these modules to detect any defects or inconsistencies that may arise when they are integrated. In this post i will just focus on the implementetion of integration testing for REST API.
Misconception Unit or Integration test ? β³
Many articles on the internet spread false information. Some explain unit test by providing integration test implementations. Thats a FATAL mistake!. The main difference between unit tests and integration tests in REST API projects is seen from whether is use external component or not ? in this case a database.
If a test uses or connect to a database it means it is an integration test NOT a Unit test. Unit tests are strictly prohibited using a real database. Everything must be mocked in unit testing even there is a database operation on the code base.
Mocha similiar with Jest is basically a JavaScript Test Framework running on Node. js and in the browser. it allows asynchronous testing, test coverage reports and use of any assertion library.Whereas Chai & Supertest is an assertion library for NodeJS and the browser that can be delightfully paired with any javascript testing framework. So in short you can use any assertion library and combine it with testing framework like mocha, jest etc as long as has the ability to call http service. But in general the biggest combo name in the integration testing field for REST API is Mocha combine with Chai and Jest combine with Supertest.
Integration Test Implementation π
As a test case in this example i will testing a simple REST API and show you how to write a test in Mocha-Chai style and Jest-Supertest style, so you can have an idea to choose which one you prefer based on the syntax style for your next project. You can get the full code from Node-Mocha & Node-Jest.
π Beware : make sure youβre using testing database when doing integration test on REST API project, donβt forget to setup .env correctly!
it("It should NOT GET all the income", (done) => { chai .request(server) .get("/api/v1/income") .end((err, res) => { res.should.have.status(404); done(); }); }); });
// ** POST /api/v1/incomes describe("POST /api/v1/incomes", () => { it("it should POST an income ", async () => { let body = { name: "sell cocacola", value: Math.floor(Math.random() * 101), }; const res = await chai.request(server).post("/api/v1/incomes").send(body); res.should.have.status(201); res.body.should.be.a("object"); res.body.should.have.property("success").eq(true); res.body.should.have.property("message"); });
it("it should not POST an income without value field", async () => { let body = { name: "buy cocacola", }; const res = await chai.request(server).post("/api/v1/incomes").send(body); res.should.have.status(400); res.body.should.be.a("object"); res.body.should.have.property("success").eq(false); res.body.should.have.property("message"); }); });
// ** GET /api/v1/incomes/:id describe("GET /api/v1/incomes/:id", () => { it("it should GET an income by id", async () => { const id = 6; const res = await chai.request(server).get(`/api/v1/incomes/${id}`); res.should.have.status(200); res.body.should.be.a("object"); res.body.should.have.property("success").eq(true); res.body.should.have.property("data").to.be.an("object"); });
it("it should not GET an income by id without numeric params", async () => { const id = "x"; const res = await chai.request(server).get(`/api/v1/incomes/${id}`); res.should.have.status(400); res.body.should.be.a("object"); res.body.should.have.property("success").eq(false); res.body.should.have.property("message"); }); });
// ** PUT /api/v1/incomes/:id describe("PUT /api/v1/incomes/:id", () => { it("it should PUT an income", async () => { // create the data first const result = await incomeService.add({ name: "income property", value: Math.floor(Math.random() * 101), }); const { id } = result.dataValues; // then update with new data const body = { name: "passive income property", value: Math.floor(Math.random() * 101), }; const res = await chai .request(server) .put(`/api/v1/incomes/${id}`) .send(body); res.should.have.status(200); res.body.should.be.a("object"); res.body.should.have.property("success").eq(true); res.body.should.have.property("message"); });
it("it should NOT PUT an income with non numeric value field", async () => { // create the data first const result = await incomeService.add({ name: "sell ticket", value: Math.floor(Math.random() * 101), }); const { id } = result.dataValues; // update with wrong data value const body = { name: "buy cocacola", value: "$900", }; const res = await chai .request(server) .put(`/api/v1/incomes/${id}`) .send(body); res.should.have.status(400); res.body.should.be.a("object"); res.body.should.have.property("success").eq(false); res.body.should.have.property("message"); }); });
// ** DELETE /api/v1/incomes/:id describe("DELETE /api/v1/incomes/:id", () => { it("it should DElETE an income", asyncfunction () { // create the data first const result = await incomeService.add({ name: "passive income property", value: Math.floor(Math.random() * 101), }); const { id } = result.dataValues; // than delete const res = await chai.request(server).delete(`/api/v1/incomes/${id}`); res.should.have.status(200); res.body.should.be.a("object"); res.body.should.have.property("success").eq(true); res.body.should.have.property("message"); });
it("it should NOT DELETE an income with invalid id", async () => { const id = 0; const res = await chai.request(server).delete(`/api/v1/incomes/${id}`); res.should.have.status(400); res.body.should.be.a("object"); res.body.should.have.property("success").eq(false); res.body.should.have.property("message"); }); }); });
// ** GET /api/v1/incomes describe("GET /api/v1/incomes", () => { it("It should GET all the income", async () => { const res = awaitrequest(server).get("/api/v1/incomes"); expect(res.statusCode).toBe(200); expect(res.statusCode).toBe(200); expect(res.body).toHaveProperty("success", true); expect(typeof res.body).toBe("object"); // * for some reason using typeOf can't dig inside object (res.body.data) // so i install 3rd party library to check data type more cleaner expect(res.body.data).toBeType("array"); });
it("It should NOT GET all the income", async () => { const res = awaitrequest(server).get("/api/v1/income"); expect(res.statusCode).toBe(404); }); });
// ** POST /api/v1/incomes describe("POST /api/v1/incomes", () => { it("it should POST an income ", async () => { let body = { name: "sell cocacola", value: Math.floor(Math.random() * 101), }; const res = awaitrequest(server).post("/api/v1/incomes").send(body); expect(res.statusCode).toBe(201); expect(typeof res.body).toBe("object"); expect(res.body).toHaveProperty("success", true); expect(res.body).toHaveProperty("message"); });
it("it should not POST an income without value field", async () => { let body = { name: "buy cocacola", }; const res = awaitrequest(server).post("/api/v1/incomes").send(body); expect(res.statusCode).toBe(400); expect(typeof res.body).toBe("object"); expect(res.body).toHaveProperty("success", false); expect(res.body).toHaveProperty("message"); }); });
// ** GET /api/v1/incomes/:id describe("GET /api/v1/incomes/:id", () => { it("it should GET an income by id", async () => { const id = 6; const res = awaitrequest(server).get(`/api/v1/incomes/${id}`); expect(res.statusCode).toBe(200); expect(typeof res.body).toBe("object"); expect(res.body).toHaveProperty("success", true); expect(res.body).toHaveProperty("data"); });
it("it should not GET an income by id without numeric params", async () => { const id = "x"; const res = awaitrequest(server).get(`/api/v1/incomes/${id}`); expect(res.statusCode).toBe(400); expect(typeof res.body).toBe("object"); expect(res.body).toHaveProperty("success", false); expect(res.body).toHaveProperty("message"); }); });
// ** PUT /api/v1/incomes/:id describe("PUT /api/v1/incomes/:id", () => { it("it should PUT an income", async () => { // create the data first const result = await incomeService.add({ name: "income property", value: Math.floor(Math.random() * 101), }); const { id } = result.dataValues; // then update with new data const body = { name: "passive income property", value: Math.floor(Math.random() * 101), }; const res = awaitrequest(server).put(`/api/v1/incomes/${id}`).send(body); expect(res.statusCode).toBe(200); expect(typeof res.body).toBe("object"); expect(res.body).toHaveProperty("success", true); expect(res.body).toHaveProperty("message"); });
it("it should NOT PUT an income with non numeric value field", async () => { // create the data first const result = await incomeService.add({ name: "sell ticket", value: Math.floor(Math.random() * 101), }); const { id } = result.dataValues; // update with wrong data value const body = { name: "buy cocacola", value: "$900", }; const res = awaitrequest(server).put(`/api/v1/incomes/${id}`).send(body); expect(res.statusCode).toBe(400); expect(typeof res.body).toBe("object"); expect(res.body).toHaveProperty("success", false); expect(res.body).toHaveProperty("message"); }); });
// ** DELETE /api/v1/incomes/:id describe("DELETE /api/v1/incomes/:id", () => { it("it should DElETE an income", asyncfunction () { // create the data first const result = await incomeService.add({ name: "passive income property", value: Math.floor(Math.random() * 101), }); const { id } = result.dataValues; // than delete const res = awaitrequest(server).delete(`/api/v1/incomes/${id}`); expect(res.statusCode).toBe(200); expect(typeof res.body).toBe("object"); expect(res.body).toHaveProperty("success", true); expect(res.body).toHaveProperty("message"); });
it("it should NOT DELETE an income with invalid id", async () => { const id = 0; const res = awaitrequest(server).delete(`/api/v1/incomes/${id}`); expect(res.statusCode).toBe(400); expect(typeof res.body).toBe("object"); expect(res.body).toHaveProperty("success", false); expect(res.body).toHaveProperty("message"); }); }); });
Conclusion π
Personally i love using Mocha if the project using Javascript and love to use Jest if the project based on Typescript. Always pick the right tool on the right time. The important point is dont forget to write integration test, peace out βοΈ