grapqhl 기반 backend에서 Query를 날리고 Network 탭을 확인하고 이를 따라해서 테스팅에 적용해보자
graphql-kr.github.io/learn/serving-over-http/#post-request에 따르면 표준 GraphQL POST 요청은 application/json content-type을 사용해야하며 아래 형식의 JSON 인코딩 바디을 포함해야합니다.
{
"query": "...",
"operationName": "...",
"variables": { "myVariable": "someValue", ... }
}
operationName 과 variables 는 옵셔널 필드입니다. operationName 은 쿼리에 여러 작업이 있는 경우에만 필요합니다.
위 내용 외에도 추가로 두 가지 경우를 지원하는 것이 좋습니다.
-
query (위 GET 예제처럼)쿼리스트링 파라미터가 있는 경우, HTTP GET의 경우와 같은 방식으로 구문 분석되고 처리되어야 합니다.
-
application/graphql Content-Type header가 있는 경우 HTTP POST body 내용을 GraphQL 쿼리스트링으로 처리합니다.
express-graphql 에는 이러한 것들이 이미 구현되어 있습니다.
직접 graphql playground에서, Query, Mutation 등을 날려보면 실제로 아래와 같은 payload가 있음을 확인하실 수 있습니다. (mutation이라고 해서 payload에 mutation을 넣는 일은 없어야겠죠 ㅋㅋ)
그러나 직접 표준 GraphQL POST 요청을 보내는 것은 보시다시피 번거롭습니다.
여기서 e2e 테스트 코드는 다음과 같이 구현될 수 있습니다.
query 부분에는 ``(백틱)이 아닌 ""를 써도 되긴 하지만 ""을 쓴다면 indent 없이 한줄로 쭉 써야해서 작성하는 코더, 협업하는 코더 둘 다 비효율적입니다. 백틱을 씁시다.
describe('createAccount', () => {
const EMAIL = 'test@email.com';
it('should create account', () => {
return request(app.getHttpServer())
.post(GRAPHQL_ENDPOINT)
.send({
query: `
mutation {
createAccount(input: {
email:"${EMAIL}", // 주의합시다. payload로 올라가는 문자열이기에 ""로 한번 더 감싸야 합니다
password:"1234",
role:Owner
}) {
ok
error
}
}
`,
})
.expect(200)
.expect((res) => {
expect(res.body.data.createAccount.ok).toBe(true);
expect(res.body.data.createAccount.error).toBe(null);
});
});
});
그런데 위 테스트 도중 한 오류를 받게 되었습니다. e2e 테스트 도중 자주 보게 될 에러 중 하나이기도 합니다.
Jest did not exit one second after the test run has completed.
This usually means that there are asynchronous operations that weren't stopped in your tests. Consider running Jest with `--detectOpenHandles` to troubleshoot this issue.
--detectOpenHandles 플래그를 붙여서 보니 got 메서드에서 문제가 생겼던 것이었고 결과적으로 다음과 같이 모킹해주었습니다. e2e 테스트에서도 mocking은 필수입니다.
jest.mock('got', () => {
return jest.fn();
});
앞서 mutation에 넘기는 인자에 ""를 붙여줬는데 반대로 숫자가 들어가야 하는 부분에서는 ""를 붙일 필요가 없습니다.
graphql 쿼리를 날리는 방식 자체보다는 이렇게 사소한 부분에서 오히려 시간을 더 잡아 먹습니다. graphql playground에서 쿼리를 날리는 부분을 먼저 잘 살펴보고 테스트 코드를 작성합니다.
it('should see a user profile', () => {
return request(app.getHttpServer())
.post(GRAPHQL_ENDPOINT)
.set('X-JWT', jwtToken)
.send({
query: `
{
userProfile(userId: ${userId}) {
ok
error
user {
id
email
}
}
}
`,
})
.expect(200)
.expect((res) => {
const { ok, error, user } = res.body.data.userProfile;
expect(ok).toBe(true);
expect(error).toBe(null);
expect(user.id).toBe(userId);
});
});
Header 붙이기
아래와 같이 post 직후 set을 통해 원하는 헤더를 붙여줄 수 있습니다.
it('should see a user profile', () => {
return request(app.getHttpServer())
.post(GRAPHQL_ENDPOINT)
.set('X-JWT', jwtToken)
.send({
query: `
{
userProfile(userId: ${userId}) {
ok
error
user {
id
email
}
}
}
`,
})
.expect(200)
.expect((res) => {
console.log(res.body.data);
const { ok, error, user } = res.body.data.userProfile;
console.log(ok, error, user);
expect(ok).toBe(200);
expect(error).toBe(null);
expect(user.id).toBe(userId);
});
});
'Node, Nest, Deno > 🦁 Nest - Series' 카테고리의 다른 글
Nest + gql + typeORM(@nestjs/typeorm) (9) Subscription (0) | 2021.01.01 |
---|---|
Nest + gql + typeORM(@nestjs/typeorm) (8) computed field (dynamic field) (0) | 2020.12.22 |
Nest + Jest + supertest e2e test (1) : setting (0) | 2020.12.13 |
Nest + Jest unit test (5) spy function, mockImplementation (0) | 2020.12.12 |
Nest + Jest unit test (4) 외부 패키지 mocking (0) | 2020.12.12 |