Skip to content

Commit 39b8ae7

Browse files
committed
Improve tests
1 parent f911999 commit 39b8ae7

File tree

14 files changed

+233
-244
lines changed

14 files changed

+233
-244
lines changed

docs/framework/angular/guides/testing.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ TestBed.configureTestingModule({
3131

3232
> If your applications actual TanStack Query config is used in unit tests, make sure `withDevtools` is not accidentally included in test providers. This can cause slow tests. It is best to keep test and production configs separate.
3333
34-
If you share helpers, remember to call `queryClient.clear()` (or build a new instance) in `afterEach` so data from one test never bleeds into another.
34+
If you share helpers, remember to call `queryClient.clear()` (or build a new instance) in `afterEach` so data from one test never bleeds into another. We generally prefer creating a fresh `QueryClient` per test: clearing only removes cached data, not custom defaults or listeners, so a reused client can leak configuration changes between specs and make failures harder to reason about. A new client keeps setup explicit and avoids any “invisible globals” influencing results.
3535

3636
## First query test
3737

@@ -63,6 +63,14 @@ await Promise.resolve()
6363
await appRef.whenStable()
6464
```
6565

66+
In the Angular Query repo we wrap this pattern in a `flushQueryUpdates()` helper (see `packages/angular-query-experimental/src/__tests__/test-utils.ts`) so every spec documents why the extra tick is required:
67+
68+
```ts
69+
TestBed.tick()
70+
await flushQueryUpdates()
71+
await appRef.whenStable()
72+
```
73+
6674
## Testing components
6775

6876
For components, bootstrap them through `TestBed.createComponent`, then await `fixture.whenStable()`:

examples/angular/infinite-query-with-max-pages/src/app/components/example.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export class ExampleComponent {
4242
)
4343

4444
readonly previousButtonDisabled = computed(
45-
() => !this.query.hasPreviousPage() || this.query.isFetchingNextPage(),
45+
() => !this.query.hasPreviousPage() || this.query.isFetchingPreviousPage(),
4646
)
4747
readonly previousButtonText = computed(() =>
4848
this.query.isFetchingPreviousPage()

packages/angular-query-experimental/src/__tests__/inject-devtools-panel.test.ts

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
1-
import {
2-
ElementRef,
3-
provideZonelessChangeDetection,
4-
signal,
5-
} from '@angular/core'
1+
import { ElementRef, signal } from '@angular/core'
62
import { TestBed } from '@angular/core/testing'
73
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
84
import { QueryClient } from '@tanstack/query-core'
9-
import { provideTanStackQuery } from '../providers'
105
import { injectDevtoolsPanel } from '../devtools-panel'
6+
import { setupTanStackQueryTestBed } from './test-utils'
117

128
const mockDevtoolsPanelInstance = {
139
mount: vi.fn(),
@@ -40,12 +36,8 @@ describe('injectDevtoolsPanel', () => {
4036
beforeEach(() => {
4137
queryClient = new QueryClient()
4238
mockElementRef = new ElementRef(document.createElement('div'))
43-
TestBed.configureTestingModule({
44-
providers: [
45-
provideZonelessChangeDetection(),
46-
provideTanStackQuery(queryClient),
47-
{ provide: ElementRef, useValue: signal(mockElementRef) },
48-
],
39+
setupTanStackQueryTestBed(queryClient, {
40+
providers: [{ provide: ElementRef, useValue: signal(mockElementRef) }],
4941
})
5042
})
5143

packages/angular-query-experimental/src/__tests__/inject-infinite-query.test.ts

Lines changed: 19 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,17 @@
11
import { TestBed } from '@angular/core/testing'
22
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'
3-
import {
4-
ChangeDetectionStrategy,
5-
Component,
6-
Injector,
7-
provideZonelessChangeDetection,
8-
} from '@angular/core'
3+
import { ChangeDetectionStrategy, Component, Injector } from '@angular/core'
94
import { sleep } from '@tanstack/query-test-utils'
10-
import { QueryClient, injectInfiniteQuery, provideTanStackQuery } from '..'
11-
import { expectSignals } from './test-utils'
5+
import { QueryClient, injectInfiniteQuery } from '..'
6+
import { expectSignals, setupTanStackQueryTestBed } from './test-utils'
127

138
describe('injectInfiniteQuery', () => {
149
let queryClient: QueryClient
1510

1611
beforeEach(() => {
1712
queryClient = new QueryClient()
1813
vi.useFakeTimers()
19-
TestBed.configureTestingModule({
20-
providers: [
21-
provideZonelessChangeDetection(),
22-
provideTanStackQuery(queryClient),
23-
],
24-
})
14+
setupTanStackQueryTestBed(queryClient)
2515
})
2616

2717
afterEach(() => {
@@ -32,7 +22,6 @@ describe('injectInfiniteQuery', () => {
3222
@Component({
3323
selector: 'app-test',
3424
template: '',
35-
standalone: true,
3625
changeDetection: ChangeDetectionStrategy.OnPush,
3726
})
3827
class TestComponent {
@@ -93,30 +82,21 @@ describe('injectInfiniteQuery', () => {
9382
test('can be used outside injection context when passing an injector', () => {
9483
const injector = TestBed.inject(Injector)
9584

96-
@Component({
97-
selector: 'app-test',
98-
template: '',
99-
standalone: true,
100-
changeDetection: ChangeDetectionStrategy.OnPush,
101-
})
102-
class TestComponent {
103-
query = injectInfiniteQuery(
104-
() => ({
105-
queryKey: ['manualInjector'],
106-
queryFn: ({ pageParam }) =>
107-
sleep(0).then(() => 'data on page ' + pageParam),
108-
initialPageParam: 0,
109-
getNextPageParam: () => 12,
110-
}),
111-
{
112-
injector: injector,
113-
},
114-
)
115-
}
116-
117-
const fixture = TestBed.createComponent(TestComponent)
118-
fixture.detectChanges()
119-
const query = fixture.componentInstance.query
85+
// Call injectInfiniteQuery directly outside any component
86+
const query = injectInfiniteQuery(
87+
() => ({
88+
queryKey: ['manualInjector'],
89+
queryFn: ({ pageParam }) =>
90+
sleep(0).then(() => 'data on page ' + pageParam),
91+
initialPageParam: 0,
92+
getNextPageParam: () => 12,
93+
}),
94+
{
95+
injector: injector,
96+
},
97+
)
98+
99+
TestBed.tick()
120100

121101
expect(query.status()).toBe('pending')
122102
})

packages/angular-query-experimental/src/__tests__/inject-is-fetching.test.ts

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
11
import { TestBed } from '@angular/core/testing'
22
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'
3-
import { Injector, provideZonelessChangeDetection } from '@angular/core'
3+
import { Injector } from '@angular/core'
44
import { sleep } from '@tanstack/query-test-utils'
5-
import {
6-
QueryClient,
7-
injectIsFetching,
8-
injectQuery,
9-
provideTanStackQuery,
10-
} from '..'
5+
import { QueryClient, injectIsFetching, injectQuery } from '..'
6+
import { setupTanStackQueryTestBed } from './test-utils'
117

128
describe('injectIsFetching', () => {
139
let queryClient: QueryClient
@@ -16,12 +12,7 @@ describe('injectIsFetching', () => {
1612
vi.useFakeTimers()
1713
queryClient = new QueryClient()
1814

19-
TestBed.configureTestingModule({
20-
providers: [
21-
provideZonelessChangeDetection(),
22-
provideTanStackQuery(queryClient),
23-
],
24-
})
15+
setupTanStackQueryTestBed(queryClient)
2516
})
2617

2718
afterEach(() => {

packages/angular-query-experimental/src/__tests__/inject-is-mutating.test.ts

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
11
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'
22
import { TestBed } from '@angular/core/testing'
3-
import { Injector, provideZonelessChangeDetection } from '@angular/core'
3+
import { Injector } from '@angular/core'
44
import { sleep } from '@tanstack/query-test-utils'
5-
import {
6-
QueryClient,
7-
injectIsMutating,
8-
injectMutation,
9-
provideTanStackQuery,
10-
} from '..'
5+
import { QueryClient, injectIsMutating, injectMutation } from '..'
6+
import { flushQueryUpdates, setupTanStackQueryTestBed } from './test-utils'
117

128
describe('injectIsMutating', () => {
139
let queryClient: QueryClient
@@ -16,12 +12,7 @@ describe('injectIsMutating', () => {
1612
vi.useFakeTimers()
1713
queryClient = new QueryClient()
1814

19-
TestBed.configureTestingModule({
20-
providers: [
21-
provideZonelessChangeDetection(),
22-
provideTanStackQuery(queryClient),
23-
],
24-
})
15+
setupTanStackQueryTestBed(queryClient)
2516
})
2617

2718
afterEach(() => {
@@ -44,7 +35,7 @@ describe('injectIsMutating', () => {
4435
})
4536

4637
expect(isMutating()).toBe(0)
47-
await vi.advanceTimersByTimeAsync(0)
38+
await flushQueryUpdates()
4839
expect(isMutating()).toBe(1)
4940
await vi.advanceTimersByTimeAsync(11)
5041
expect(isMutating()).toBe(0)

packages/angular-query-experimental/src/__tests__/inject-mutation-state.test.ts

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,14 @@ import {
88
} from '@angular/core'
99
import { TestBed } from '@angular/core/testing'
1010
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'
11-
import { By } from '@angular/platform-browser'
1211
import { sleep } from '@tanstack/query-test-utils'
1312
import {
1413
QueryClient,
1514
injectMutation,
1615
injectMutationState,
1716
provideTanStackQuery,
1817
} from '..'
19-
import { setFixtureSignalInputs } from './test-utils'
18+
import { registerSignalInput } from './test-utils'
2019

2120
describe('injectMutationState', () => {
2221
let queryClient: QueryClient
@@ -159,23 +158,35 @@ describe('injectMutationState', () => {
159158
}))
160159
}
161160

162-
const fixture = TestBed.createComponent(FakeComponent)
163-
const { debugElement } = fixture
164-
setFixtureSignalInputs(fixture, { name: fakeName })
161+
registerSignalInput(FakeComponent, 'name')
162+
163+
@Component({
164+
template: `<app-fake [name]="name()" />`,
165+
imports: [FakeComponent],
166+
})
167+
class HostComponent {
168+
protected readonly name = signal(fakeName)
169+
}
170+
171+
const fixture = TestBed.createComponent(HostComponent)
172+
fixture.detectChanges()
165173
await vi.advanceTimersByTimeAsync(0)
166174

167-
let spans = debugElement
168-
.queryAll(By.css('span'))
169-
.map((span) => span.nativeNode.textContent)
175+
const readSpans = () =>
176+
Array.from(
177+
fixture.nativeElement.querySelectorAll(
178+
'span',
179+
) as NodeListOf<HTMLSpanElement>,
180+
).map((span) => span.textContent)
181+
182+
let spans = readSpans()
170183

171184
expect(spans).toEqual(['pending', 'pending'])
172185

173186
await vi.advanceTimersByTimeAsync(11)
174187
fixture.detectChanges()
175188

176-
spans = debugElement
177-
.queryAll(By.css('span'))
178-
.map((span) => span.nativeNode.textContent)
189+
spans = readSpans()
179190

180191
expect(spans).toEqual(['success', 'error'])
181192
})

packages/angular-query-experimental/src/__tests__/inject-mutation.test.ts

Lines changed: 49 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,9 @@ import {
99
} from '@angular/core'
1010
import { TestBed } from '@angular/core/testing'
1111
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'
12-
import { By } from '@angular/platform-browser'
1312
import { sleep } from '@tanstack/query-test-utils'
1413
import { QueryClient, injectMutation, provideTanStackQuery } from '..'
15-
import { expectSignals, setFixtureSignalInputs } from './test-utils'
14+
import { expectSignals, registerSignalInput } from './test-utils'
1615

1716
describe('injectMutation', () => {
1817
let queryClient: QueryClient
@@ -323,19 +322,32 @@ describe('injectMutation', () => {
323322
}
324323
}
325324

326-
const fixture = TestBed.createComponent(FakeComponent)
327-
const { debugElement } = fixture
328-
setFixtureSignalInputs(fixture, { name: 'value' })
325+
registerSignalInput(FakeComponent, 'name')
329326

330-
const button = debugElement.query(By.css('button'))
331-
button.triggerEventHandler('click')
327+
@Component({
328+
template: `<app-fake [name]="name()" />`,
329+
imports: [FakeComponent],
330+
})
331+
class HostComponent {
332+
protected readonly name = signal('value')
333+
}
334+
335+
const fixture = TestBed.createComponent(HostComponent)
336+
fixture.detectChanges()
337+
338+
const hostButton = fixture.nativeElement.querySelector(
339+
'button',
340+
) as HTMLButtonElement
341+
hostButton.click()
332342

333343
await vi.advanceTimersByTimeAsync(11)
334344
fixture.detectChanges()
335345

336-
const text = debugElement.query(By.css('span')).nativeElement.textContent
337-
expect(text).toEqual('value')
338-
const mutation = mutationCache.find({ mutationKey: ['fake', 'value'] })
346+
const span = fixture.nativeElement.querySelector('span') as HTMLSpanElement
347+
expect(span.textContent).toEqual('value')
348+
const mutation = mutationCache.find({
349+
mutationKey: ['fake', 'value'],
350+
})
339351
expect(mutation).toBeDefined()
340352
expect(mutation!.options.mutationKey).toStrictEqual(['fake', 'value'])
341353
})
@@ -364,26 +376,43 @@ describe('injectMutation', () => {
364376
}
365377
}
366378

367-
const fixture = TestBed.createComponent(FakeComponent)
368-
const { debugElement } = fixture
369-
setFixtureSignalInputs(fixture, { name: 'value' })
379+
registerSignalInput(FakeComponent, 'name')
370380

371-
const button = debugElement.query(By.css('button'))
372-
const span = debugElement.query(By.css('span'))
381+
@Component({
382+
template: `<app-fake [name]="name()" />`,
383+
imports: [FakeComponent],
384+
})
385+
class HostComponent {
386+
protected readonly name = signal('value')
387+
388+
updateName(value: string): void {
389+
this.name.set(value)
390+
}
391+
}
373392

374-
button.triggerEventHandler('click')
393+
const fixture = TestBed.createComponent(HostComponent)
394+
fixture.detectChanges()
395+
396+
let button = fixture.nativeElement.querySelector(
397+
'button',
398+
) as HTMLButtonElement
399+
button.click()
375400
await vi.advanceTimersByTimeAsync(11)
376401
fixture.detectChanges()
377402

378-
expect(span.nativeElement.textContent).toEqual('value')
403+
let span = fixture.nativeElement.querySelector('span') as HTMLSpanElement
404+
expect(span.textContent).toEqual('value')
379405

380-
setFixtureSignalInputs(fixture, { name: 'updatedValue' })
406+
fixture.componentInstance.updateName('updatedValue')
407+
fixture.detectChanges()
381408

382-
button.triggerEventHandler('click')
409+
button = fixture.nativeElement.querySelector('button') as HTMLButtonElement
410+
button.click()
383411
await vi.advanceTimersByTimeAsync(11)
384412
fixture.detectChanges()
385413

386-
expect(span.nativeElement.textContent).toEqual('updatedValue')
414+
span = fixture.nativeElement.querySelector('span') as HTMLSpanElement
415+
expect(span.textContent).toEqual('updatedValue')
387416

388417
const mutations = mutationCache.findAll()
389418
expect(mutations.length).toBe(2)

0 commit comments

Comments
 (0)