테스트
TanStack Query를 사용하는 대부분의 Angular 테스트에는 injectQuery/injectMutation을 호출하는 서비스 또는 컴포넌트가 포함됩니다.
TanStack Query의 inject* 함수는 PendingTasks와 통합되어 프레임워크가 진행 중인 쿼리와 뮤테이션을 인식하도록 보장합니다.
즉, 테스트와 SSR은 뮤테이션과 쿼리가 이행될 때까지 기다릴 수 있습니다. 단위 테스트에서는 ApplicationRef.whenStable() 또는 fixture.whenStable()을 사용하여 쿼리 완료를 기다릴 수 있습니다. 이는 Zone.js 및 Zoneless 설정 모두에서 작동합니다.
이 통합에는 Angular 19 이상이 필요합니다. 이전 버전의 Angular는
PendingTasks를 지원하지 않습니다.
TestBed 설정
각 spec마다 새로운 QueryClient를 생성하고 provideTanStackQuery 또는 provideQueryClient와 함께 제공하세요. 이렇게 하면 캐시가 서로 격리되고 테스트마다 기본 옵션을 변경할 수 있습니다:
const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: false, // ✅ faster failure tests
},
},
})
TestBed.configureTestingModule({
providers: [provideTanStackQuery(queryClient)],
})
애플리케이션의 실제 TanStack Query 구성을 단위 테스트에서 사용하는 경우, 테스트 provider에
withDevtools가 실수로 포함되지 않았는지 확인합니다. 이로 인해 테스트가 느려질 수 있습니다. 테스트 구성과 프로덕션 구성은 분리하는 것이 가장 좋습니다.
헬퍼를 공유하는 경우 한 테스트의 데이터가 다른 테스트로 절대 유입되지 않도록 afterEach에서 queryClient.clear()를 호출하거나 새 인스턴스를 생성해야 합니다.
첫 번째 쿼리 테스트
쿼리 테스트는 일반적으로 TestBed.runInInjectionContext 내부에서 실행한 다음 안정화될 때까지 기다립니다:
const appRef = TestBed.inject(ApplicationRef)
const query = TestBed.runInInjectionContext(() =>
injectQuery(() => ({
queryKey: ['greeting'],
queryFn: () => 'Hello',
})),
)
TestBed.tick() // Trigger effect
// Application is stable when queries are idle
await appRef.whenStable()
expect(query.status()).toBe('success')
expect(query.data()).toBe('Hello')
PendingTasks를 사용하면 쿼리가 완료된 후 whenStable()이 이행됩니다. 가짜 타이머(Vitest)를 사용할 때는 안정성을 기다리기 전에 시계와 마이크로태스크 하나를 진행합니다:
await vi.advanceTimersByTimeAsync(0)
await Promise.resolve()
await appRef.whenStable()
컴포넌트 테스트하기
컴포넌트의 경우 TestBed.createComponent를 통해 부트스트랩한 다음, fixture.whenStable()을 await합니다:
const fixture = TestBed.createComponent(ExampleComponent)
await fixture.whenStable()
expect(fixture.componentInstance.query.data()).toEqual({ value: 42 })
재시도 처리
기본 백오프가 세 번 실행되므로 재시도는 실패하는 테스트를 느리게 합니다. 테스트를 빠르게 유지하려면 defaultOptions를 통해 또는 쿼리별로 retry: false(또는 특정 숫자)을 설정하세요. 쿼리가 의도적으로 재시도하는 경우 중간 횟수 대신 최종 상태를 검증하세요.
HttpClient 및 네트워크 스텁
Angular의 HttpClientTestingModule은 PendingTasks와 원활하게 연동됩니다. Query provider와 함께 등록하고 HttpTestingController를 통해 응답을 플러시하세요:
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [provideTanStackQuery(queryClient)],
})
const httpCtrl = TestBed.inject(HttpTestingController)
const query = TestBed.runInInjectionContext(() =>
injectQuery(() => ({
queryKey: ['todos'],
queryFn: () => lastValueFrom(TestBed.inject(HttpClient).get('/api/todos')),
})),
)
const fixturePromise = TestBed.inject(ApplicationRef).whenStable()
httpCtrl.expectOne('/api/todos').flush([{ id: 1 }])
await fixturePromise
expect(query.data()).toEqual([{ id: 1 }])
httpCtrl.verify()
무한 쿼리 및 페이지네이션
무한 쿼리에도 같은 패턴을 사용합니다. fetchNextPage()를 호출하고, 시간을 모의로 설정했다면 타이머를 진행한 다음, 안정화될 때까지 기다리고 data().pages를 단언합니다.
const infinite = TestBed.runInInjectionContext(() =>
injectInfiniteQuery(() => ({
queryKey: ['pages'],
queryFn: ({ pageParam = 1 }) => fetchPage(pageParam),
getNextPageParam: (last, all) => all.length + 1,
})),
)
await appRef.whenStable()
expect(infinite.data().pages).toHaveLength(1)
await infinite.fetchNextPage()
await vi.advanceTimersByTimeAsync(0)
await appRef.whenStable()
expect(infinite.data().pages).toHaveLength(2)
뮤테이션과 낙관적 업데이트
const mutation = TestBed.runInInjectionContext(() =>
injectMutation(() => ({
mutationFn: async (input: string) => input.toUpperCase(),
})),
)
mutation.mutate('test')
// Trigger effect
TestBed.tick()
await appRef.whenStable()
expect(mutation.isSuccess()).toBe(true)
expect(mutation.data()).toBe('TEST')
빠른 체크리스트
- 테스트마다 새로운
QueryClient를 사용합니다(이후 삭제합니다) - 시간 초과를 방지하도록 재시도를 비활성화하거나 제어합니다
- 가짜 타이머를 사용할 때
whenStable()전에 타이머와 마이크로태스크를 진행합니다 - 네트워크 호출을 검증하려면
HttpClientTestingModule또는 선호하는 mock을 사용합니다 - 각
refetch,fetchNextPage또는 뮤테이션 후에whenStable()을 await합니다 - 서비스 테스트에는
TestBed.runInInjectionContext를, 컴포넌트 테스트에는fixture.whenStable()을 사용하는 것이 좋습니다