[TIL] Integration Test using Supertest
05/10/23
![[TIL] Integration Test using Supertest](/_next/image?url=https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1688360446129%2F332e3fba-3c56-470d-bb90-fb23ab083024.png&w=3840&q=75)
๐ Issues encountered
Issue #1
Using supertest to test login and auth middleware.
Issue #2
Creating test cases for unauthorized access to modify or delete resources.
Issue #3
Refactoring related to Issues #1 and #2.
๐ What I tried
Issue #1
Define an object to hold userData.
Login API test:
test("POST /api/auth/login API (login) Integration Test", async () => {
const loginRequestBodyParams = {
nickname: userData.nickname,
password: userData.password,
};
const response = await supertest(app)
.post(`/api/auth/login`) // API's HTTP Method & URL
.send(loginRequestBodyParams); // Request Body
/** Validation logic for POST /api/auth/login API **/
// 1. If the login is successful, it should return a 200 HTTP status code.
expect(response.status).toEqual(200);
// 2. After the API call is completed, it should pass the Authorization and refreshtoken values as body data.
userData.accessToken = response.body.Authorization;
userData.refreshToken = response.body.refreshtoken;
});
The login API test stores the access token and refresh token in the userData object. 3) Mocking the auth middleware:
test("POST /api/worldcup API (createWorldcup) Integration Test Error Case, Invalid Params Error", async () => {
const createWorldcupRequestBodyParamsByInvalidParamsError = {
title: "Title_InvalidParamsError",
content: "Content_InvalidParamsError",
};
const response = await supertest(app)
.post(`/api/worldcup`) // API's HTTP Method & URL
.set("Authorization", `Bearer ${userData.accessToken}`) // Set Access Token in the header
.set("refreshtoken", `${userData.refreshToken}`) // Set Refresh Token in the header
.send(createWorldcupRequestBodyParamsByInvalidParamsError); // Request Body
/** Validation logic for POST /api/worldcup API error case **/
// 1. If the InvalidParamsError occurs during the API call, it should return a 412 HTTP status code.
expect(response.status).toEqual(412);
// 2. The API response should have the format { errorMessage: '"choices" is required' }.
expect(response.body).toMatchObject({
errorMessage: '"choices" is required',
});
});
The
set()function is used to set data in the header. Since the token information is included in the header during login, the auth middleware expects to retrieve both tokens from the header.The above code mocks the process of retrieving the two tokens from the header in the auth middleware.
Issue #2
I signed up and logged in with a different user.
Initially, I performed delete with user1, but there were no worldcup post data to compare, so I modified the logic to have user1 create two worldcups instead.
Is there a better way to approach this?
Issue #3
supertest allows the use of an agent, a virtual user!
In particular, when I initially attempted integration testing without an agent, I got stuck in the part where the auth middleware is processed. However, supertest provides a method called
agent()that behaves similarly to a browser sending cookie values by default.In other words, an agent can maintain the login state by persistently sending requests!
The usage of agent() is as follows:
describe('POST /api/auth/login', () => {
const agent = request
.agent(app);
beforeEach((done) => {
agent.post('/api/auth/login')
.send({
nickname: userData.nickname,
password: userData.password,
email: userData.email,
})
.end(done);
});
test("POST /api/worldcup", async () => {
const createWorldcupRequestBodyParams = {
title: worldcupData2.title,
content: worldcupData2.content,
choices: worldcupData2.choices,
};
const response = await agent
.post(`/api/worldcup`)
.send(createWorldcupRequestBodyParams);
expect(response.status).toEqual(201)
expect(response.body).toMatchObject({
newWorldcup: {
worldcup_id: worldcupData2.worldcup_id,
user_id: userData.user_id,
title: createWorldcupRequestBodyParams.title,
content: createWorldcupRequestBodyParams.content,
choices: createWorldcupRequestBodyParams.choices,
},
});
});
});
The logic that performs login is placed inside the
beforeEach()function before each test.
๐ What I newly learned
Integration testing with supertest
Test-Driven Development (TDD)
Nodemailer
Design pattern - Creational pattern
๐ What to learn next
CI/CD
Troubleshooting presentation section summary
Storytelling presentation summary
![[์ฝํ
] ๊ทธ๋ฆฌ๋ ๋ฌธ์ - ๋ฌด์ง์ ๋จน๋ฐฉ ๋ผ์ด๋ธ](/_next/image?url=https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1712215455263%2F1ac1f35a-8862-4e42-8d0c-e2bea01e04c0.png&w=3840&q=75)
![[์ฝํ
] Bfs ํ ๋งํ](/_next/image?url=https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1709032619170%2F70056896-c857-444b-9c99-45bfcb466806.png&w=3840&q=75)
![[์ฝํ
] Dfs ๋ฌธ์ ์ ํ - ๊ทธ๋ํ ๋ด์์ ๊ตฌ๋ถํ์ฌ ์นด์ดํธ ํ๊ธฐ](/_next/image?url=https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1709019361383%2Fb0585d72-c808-4169-83a9-2724f312e927.png&w=3840&q=75)
![[์ฝํ
] DFS vs BFS](/_next/image?url=https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1708971211123%2F71f9386c-6a62-43b2-a602-4d084c24d6cf.png&w=3840&q=75)
![[์ฝํ
] ์ฌํ๊ฒฝ๋ก](/_next/image?url=https%3A%2F%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1708971251412%2F27ce72ed-8ee7-4d13-a02f-ff4bbe50c4be.png&w=3840&q=75)