You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Angular v19+Signals中编写TanStack Angular Query的测试用例

Angular v19 + TanStack Query (Signals) 测试方案指引

测试环境准备

  • 确保项目已安装 @tanstack/angular-query-experimental(对应Angular Signals版本)及 @tanstack/query-core
  • 测试模块中通过 provideAngularQuery 配置测试专用QueryClient,关闭重试、缓存时间等干扰测试的默认配置

基础组件测试示例

假设你有如下使用 injectQuery 的组件:

@Component({
  selector: 'app-user-view',
  template: `
    <div *ngIf="data().isLoading">Loading...</div>
    <div *ngIf="data().error">{{ data().error.message }}</div>
    <div *ngIf="data().data">{{ data().data.name }}</div>
  `,
  standalone: true,
})
export class UserViewComponent {
  private api = inject(UserApiService);
  data = injectQuery(() => ({
    queryKey: ['user'],
    queryFn: () => this.api.getCurrentUser(),
  }));
}

对应的测试用例:

import { TestBed } from '@angular/core/testing';
import { UserViewComponent } from './user-view.component';
import { UserApiService } from './user-api.service';
import { provideAngularQuery } from '@tanstack/angular-query-experimental';
import { QueryClient } from '@tanstack/query-core';

describe('UserViewComponent', () => {
  let apiSpy: jasmine.SpyObj<UserApiService>;
  let queryClient: QueryClient;

  beforeEach(async () => {
    apiSpy = jasmine.createSpyObj('UserApiService', ['getCurrentUser']);
    
    // 初始化测试用QueryClient
    queryClient = new QueryClient({
      defaultOptions: {
        queries: { retry: false, staleTime: 0 },
      },
    });

    await TestBed.configureTestingModule({
      imports: [UserViewComponent],
      providers: [
        provideAngularQuery(() => ({ queryClient })),
        { provide: UserApiService, useValue: apiSpy },
      ],
    }).compileComponents();
  });

  it('should render loading state initially', () => {
    const fixture = TestBed.createComponent(UserViewComponent);
    fixture.detectChanges();
    expect(fixture.nativeElement.textContent).toContain('Loading...');
  });

  it('should render user data on query success', async () => {
    const mockUser = { name: 'Alice' };
    apiSpy.getCurrentUser.and.returnValue(Promise.resolve(mockUser));

    const fixture = TestBed.createComponent(UserViewComponent);
    fixture.detectChanges();

    // 等待Query完成并更新Signal
    await queryClient.refetchQueries({ queryKey: ['user'] });
    fixture.detectChanges();

    expect(fixture.nativeElement.textContent).toContain('Alice');
    expect(apiSpy.getCurrentUser).toHaveBeenCalledTimes(1);
  });

  it('should render error message on query failure', async () => {
    apiSpy.getCurrentUser.and.returnValue(Promise.reject(new Error('Failed to fetch user')));

    const fixture = TestBed.createComponent(UserViewComponent);
    fixture.detectChanges();

    await queryClient.refetchQueries({ queryKey: ['user'] });
    fixture.detectChanges();

    expect(fixture.nativeElement.textContent).toContain('Failed to fetch user');
  });
});

缓存与Signal更新测试

直接操作QueryClient缓存验证Signal响应性:

it('should update view when cache data changes', () => {
  // 预设置缓存数据
  queryClient.setQueryData(['user'], { name: 'Bob' });

  const fixture = TestBed.createComponent(UserViewComponent);
  fixture.detectChanges();
  expect(fixture.nativeElement.textContent).toContain('Bob');

  // 更新缓存,Signal自动同步
  queryClient.setQueryData(['user'], { name: 'Charlie' });
  fixture.detectChanges();
  expect(fixture.nativeElement.textContent).toContain('Charlie');
});

常见问题处理

  • Signal未触发视图更新:异步操作后必须调用 fixture.detectChanges(),或用 await fixture.whenStable() 等待所有异步任务完成
  • Query未自动执行:检查QueryClient配置,确保未设置 enabled: false,或手动调用 queryClient.fetchQuery({ queryKey: ['user'], queryFn: ... }) 触发请求
  • 依赖注入报错:确保测试模块中通过 provideAngularQuery 正确传入测试用QueryClient,而非使用生产环境配置

内容的提问来源于stack exchange,提问作者Eternal Sunshine

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 09:41:04