Remix 앱 로더에 대한 첫 번째 단위 테스트 만들기
여기서 단계별로 로더를 단위 테스트하는 한 가지 방법을 보여드리겠습니다. 이 글을 쓰는 시점에는 Remix 코드가 있는 구성 요소를 테스트하는 표준 방법이 없습니다. 따라서 우리는 비즈니스 로직, 로더 및 작업을 개별적으로 테스트하고 있습니다.
이 문서에서는 인증 없는 샘플 테스트를 보여줍니다. 우리 자신의 인증은 뚜렷하고 큰 주제입니다. 테스트 인증 설정은 독자에게 연습으로 남겨둡니다 😬
E2E에 관심이 있다면 remix-forms repo에서 예제를 볼 수 있습니다.
우리가 만들고 있는 것
Remix 문서를 처음 따를 때 빌드한 the remix-jokes app을 사용하겠습니다. 당시에는 리믹스 스택이 없었기 때문에 새로 설치했습니다. 😅 이 앱은 단위 테스트를 실행하기 위해 Express 서버, Postgres DB 및 Jest를 사용합니다.
설정
경로 파일을 테스트합니다. 이러한 테스트 파일의 가장 좋은 위치는 경로 자체에 가깝습니다. 하지만 테스트 파일이 경로가 아니라고 Remix에 알려야 합니다. 그렇지 않으면 서버가 중단됩니다. Remix 구성에
ignoredRouteFiles
를 추가하면 모두 좋습니다.// remix.config.js
module.exports = {
...
ignoredRouteFiles: ['.*', '**/__tests__/**'],
}
첫 번째 테스트 추가(및 코너 케이스 발견)
테스트해봅시다
app/routes/jokes/$jokeId.tsx
.//app/routes/jokes/$jokeId.tsx
export const loader: LoaderFunction = async ({ request, params }) => {
const userId = await getUserId(request)
const joke = await db.joke.findUnique({
where: { id: params.jokeId },
})
if (!joke) {
throw new Response('What a joke! Not found.', {
status: 404,
})
}
const data: LoaderData = { joke, isOwner: joke.jokester_id === userId }
return data
}
로더는 명명된 내보내기이므로 다른 기능으로 가져올 수 있습니다. 하지만 다시 어떻게 부릅니까? 😕
// app/routes/jokes/__tests__/jokeid.test.ts
import { loader } from '../$jokeId'
describe('loader', () => {
it('first try', async () => {
// What params do we need to call the loader?
expect(loader()).toEqual('foo') // does not work, of course
})
})
구조에 타이프 스크립트!
request
, context
및 params
3개의 매개변수가 필요합니다.Request 클래스를 사용하기 위한 Jest 환경must be set to "node". 함수 호출이 작동하는지 확인하기 위해 더미 테스트를 추가해 보겠습니다.
// app/routes/jokes/__tests__/jokeid.test.ts
import { loader } from '../$jokeId'
describe('loader', () => {
it('second try', async () => {
const request = new Request('http://foo.ber')
const response = await loader({ request, context: {}, params: {} })
expect(true).toBe(true)
})
})
💣💥
우리는
id
매개변수 없이 로더를 호출했는데 폭발했습니다. 이 경로에는 항상 ID가 있으므로 함수 호출이 유효하지 않기 때문에 이전에는 함수가 해당 사례를 처리하지 않았습니다. 아이디를 추가해보겠습니다.// app/routes/jokes/__tests__/jokeid.test.ts
import { loader } from '../$jokeId'
describe('loader', () => {
it('second try', async () => {
const request = new Request('http://foo.ber')
const response = await loader({ request, context: {}, params: { jokeId: 'foo' } })
expect(true).toBe(true)
})
})
오류가 변경되었으므로 진행 상황입니다.
Invalid prisma.joke.findUnique() invocation:
Inconsistent column data: Error creating UUID, invalid length: expected one of [36, 32], found 3
jokeId
매개변수는 특정 길이를 가져야 합니다. 코드에서 해당 사례를 처리해 보겠습니다.//app/routes/jokes/$jokeId.tsx
export const loader: LoaderFunction = async ({ request, params }) => {
const userId = await getUserId(request)
const jokeId = params.jokeId || ''
if (![32, 36].includes(jokeId.length)) {
throw new Response('Joke id must be 32 or 36 characters', { status: 400 })
}
...
}
이제 첫 번째 실제 테스트를 실행할 수 있습니다.
// app/routes/jokes/__tests__/jokeid.test.ts
describe('loader', () => {
it('fails with an invalid id', async () => {
const request = new Request('http://foo.ber')
try {
await loader({ request, context: {}, params: { jokeId: 'foo' } })
} catch (error) {
expect((error as Response).status).toEqual(400)
}
// Todo: assert loader has thrown
})
})
하지만 주의 사항이 있습니다. 어떤 이유로
catch
블록에 속하지 않으면 이 테스트에 expect
가 없고 녹색이 됩니다. 이를 처리하는 방법에는 여러 가지가 있으며 그 중 하나는 다음과 같습니다.// app/routes/jokes/__tests__/jokeid.test.ts
...
it('fails with an invalid id', async () => {
const request = new Request('http://foo.bar')
let result
try {
await loader({ request, context: {}, params: { jokeId: 'foo' } })
} catch (error) {
result = error
}
expect((result as Response).status).toEqual(400)
})
예를 들어
result
가 null이 아니라고 주장할 수도 있습니다. 그러나이 솔루션은 충분해 보입니다.테스트 '찾을 수 없음'
위의 테스트와 매우 유사합니다. 가짜 임의 ID를 사용하면 트릭을 수행합니다.
// app/routes/jokes/__tests__/jokeid.test.ts
...
it('returns 404 when joke is not found', async () => {
const request = new Request('http://foo.bar')
let result
try {
await loader({
request,
context: {},
params: { jokeId: '49ed1af0-d122-4c56-ac8c-b7a5f033de88' },
})
} catch (error) {
result = error
}
expect((result as Response).status).toEqual(404)
})
행복한 길을 시험하다
이것은 데이터베이스에 무언가가 있는 한 간단합니다.
// app/routes/jokes/__tests__/jokeid.test.ts
...
it('returns the joke when it is found', async () => {
const request = new Request('http://foo.bar')
const jokes = await db.joke.findMany({ take: 1 })
const joke = jokes[0]
const { id } = joke
let result
result = await loader({
request,
context: {},
params: { jokeId: id },
})
expect(result).toEqual({ joke, isOwner: false })
}
물론 특정 DB 데이터에 의존하는 것은 프로젝트가 커짐에 따라 문제가 발생할 수 있습니다. Prisma docs은 클라이언트를 조롱할 것을 권장합니다.
전체 테스트 파일:
// app/routes/jokes/__tests__/jokeid.test.ts
import { loader } from '../$jokeId'
import { db } from '~/utils/db.server'
describe('loader', () => {
it('fails with an invalid id', async () => {
const request = new Request('http://foo.bar')
let result
try {
await loader({
request,
context: {},
params: { jokeId: 'foo' },
})
} catch (error) {
result = error
}
expect((result as Response).status).toEqual(400)
})
it('returns not found when joke is not found', async () => {
const request = new Request('http://foo.bar')
let result
try {
await loader({
request,
context: {},
params: { jokeId: '49ed1af0-d122-4c56-ac8c-b7a5f033de88' },
})
} catch (error) {
result = error
}
expect((result as Response).status).toEqual(404)
})
it('returns the joke when it is found', async () => {
const request = new Request('http://foo.bar')
const jokes = await db.joke.findMany({ take: 1 })
const joke = jokes[0]
const { id } = joke
let result
result = await loader({
request,
context: {},
params: { jokeId: id },
})
expect(result).toEqual({ joke, isOwner: false })
})
})
우리는 결국 다른 접근 방식을 사용하게 될 것입니다. 각 테스트 실행에 대해 재설정되는 테스트 데이터베이스를 만드는 것입니다. 그러나 그것은 향후 게시물의 주제입니다!
첫 로더 테스트 작성이 즐거웠기를 바랍니다. 😊
Reference
이 문제에 관하여(Remix 앱 로더에 대한 첫 번째 단위 테스트 만들기), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/felipefreitag/create-the-first-unit-test-for-your-remix-app-loaders-1fm6텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)