在不影响其他测试用例的情况下,更新某个单元测试的信号值

前端开发 2026-07-10

我有一个来自服务注入的信号,我想测试在该值具有特定属性时的场景。

以下是规格定义:

describe('MyComponent', () => {
  let component: MyComponent;
  let fixture: ComponentFixture<MyComponent>;

  // This is the spy for the service I'm injecting to get the signal from
  let myServiceSpy: Spy<MyService>;

  const mockSignalValue = {someProperty: ''};
  const mockSignalValueWithExtraProperty = {...mockSignalValue, extraProperty: ''};

  beforeEach(async () => {
    myServiceSpy = createSpyFromClass(MyService);
    Object.defineProperty(myServiceSpy, 'mySignal', {
      get: () => signal(mockSignalValue),
      configurable: true,
    });

    await TestBed.configureTestingModule({
      imports: [MyComponent];
      providers: [
        { provide: MyService, useValue: myServiceSpy },
      ],
    }).compileComponents;

    fixture = TestBed.createComponent(MyComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });
});

在组件的实现中,这个信号来自服务:

export class MyComponent {
  mySignal: Signal<any> = this.myService.mySignal;

  constructor(myService: MyService) {}
}

我想测试当信号具有特定值时,组件的行为是否正确,以及在没有该值时的行为。为了简化本示例,我只测试信号值是否具备某个属性。

没有该属性:

it('should not have the extra property', () => {
  expect(component.mySignal.extraProperty).toEqual(undefined);
}

我已经尝试了以下方法来在不影响其他测试的前提下,为这个特定测试更新信号值:

it('should have the extra property', () => {
  Object.defineProperty(myServiceSpy, 'mySignal', {
    // I've tried with both get & set
    get: () => signal(mockSignalValueWithExtraProperty),
    configurable: true,
  });

  spyOnProperty(myServiceSpy, 'mySignal', 'get')
    .and.returnValue(signal(mockSignalValueWithExtraProperty));

  // Neither of these ways worked to update the signal value for this test

  fixture.detectChanges();

  expect(component.mySignal.extraProperty).toEqual('');
}

解决方案

我们在 beforeEach 中用信号来初始化服务,随后把它配置成组件的属性,因此对服务信号的修改不会传播到组件。

相反,我们可以在测试用例初始化时初始化一个信号,并将其作为服务中的值。

对于第二个测试用例,我们可以使用 set 方法来修改返回的模拟值,这将解决这个问题。

创建一个用来保存该信号的属性:

let mySignalMock: WritableSignal<any>; // <- changed here!

然后用这个属性来初始化信号,该信号将在每个测试用例中被修改:

  beforeEach(async () => {
    mySignalMock = signal(mockSignalValue);
    myServiceSpy = createSpyFromClass(TestService);
    Object.defineProperty(myServiceSpy, 'mySignal', {
      get: () => mySignalMock, // <- changed here!
      configurable: true,
    });

对于第一个测试用例,它将照常通过:

it('should not have the extra property', () => {
  expect(component.mySignal().extraProperty).toEqual(undefined);
});

对于第二个测试用例,使用 set 方法修改返回的值。

it('should have the extra property', () => {
  mySignalMock.set(mockSignalValueWithExtraProperty); // <- changed here!
  fixture.detectChanges();
  expect(component.mySignal().extraProperty).toEqual('');
});

完整代码:

import { ComponentFixture, TestBed } from '@angular/core/testing';
import { signal, WritableSignal } from '@angular/core';
import {
  BrowserTestingModule,
  platformBrowserTesting,
} from '@angular/platform-browser/testing';

import { describe, it, expect, beforeAll, vi, Mock, beforeEach } from 'vitest';
import { TestComponent } from '../src/app/footer/test.component';
import { TestService } from '../src/app/footer/test.service';
import { createSpyFromClass, Spy } from '@bugsplat/vitest-auto-spies';

describe('TestComponent', () => {
  let component: TestComponent;
  let fixture: ComponentFixture<TestComponent>;
  let mySignalMock: WritableSignal<any>; // <- changed here!

  // This is the spy for the service I'm injecting to get the signal from
  let myServiceSpy: Spy<TestService>;

  const mockSignalValue = { someProperty: '' };
  const mockSignalValueWithExtraProperty = {
    ...mockSignalValue,
    extraProperty: '',
  };

  beforeAll(() => {
    TestBed.initTestEnvironment(BrowserTestingModule, platformBrowserTesting());
  });

  beforeEach(async () => {
    mySignalMock = signal(mockSignalValue);
    myServiceSpy = createSpyFromClass(TestService);
    Object.defineProperty(myServiceSpy, 'mySignal', {
      get: () => mySignalMock, // <- changed here!
      configurable: true,
    });

    await TestBed.configureTestingModule({
      imports: [TestComponent],
      providers: [{ provide: TestService, useValue: myServiceSpy }],
    }).compileComponents();

    fixture = TestBed.createComponent(TestComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', async () => {
    expect(component).toBeTruthy();
  });

  it('should not have the extra property', () => {
    expect(component.mySignal().extraProperty).toEqual(undefined);
  });

  it('should have the extra property', () => {
    mySignalMock.set(mockSignalValueWithExtraProperty); // <- changed here!
    fixture.detectChanges();
    expect(component.mySignal().extraProperty).toEqual('');
  });
});

Stackblitz演示

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章