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

如何在不影响其他单元测试的情况下更新特定测试的Signal值

Angular单元测试:更新注入服务的Signal值

问题背景

需要测试组件中从注入服务获取的Signal值包含特定属性的场景。组件在构造时直接赋值了服务的Signal,但在单个测试用例中更新服务的Signal值时,组件的Signal未同步更新,导致测试失败。

组件实现

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

  constructor(myService: MyService) {}
}

现有测试代码

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

  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();
  });

  it('should not have the extra property', () => {
    // 注意:Signal需要调用()才能获取值
    expect(component.mySignal().extraProperty).toEqual(undefined);
  });

  it('should have the extra property', () => {
    // 尝试过的两种方法均未生效
    Object.defineProperty(myServiceSpy, 'mySignal', {
      get: () => signal(mockSignalValueWithExtraProperty),
      configurable: true,
    });

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

    fixture.detectChanges();

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

问题原因

组件在构造函数执行阶段就直接持有了服务mySignal属性的Signal引用,后续即使重新定义服务的mySignal属性,组件内的变量不会自动更新为新的Signal实例。

解决方法

方案1:测试用例中重新创建组件实例

在需要更新Signal的测试用例里,重新生成组件实例,让组件重新读取服务的最新Signal:

it('should have the extra property', () => {
  // 重新定义服务的mySignal属性
  Object.defineProperty(myServiceSpy, 'mySignal', {
    get: () => signal(mockSignalValueWithExtraProperty),
    configurable: true,
  });

  // 重新创建组件实例,触发构造函数重新读取Signal
  fixture = TestBed.createComponent(MyComponent);
  component = fixture.componentInstance;
  fixture.detectChanges();

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

方案2:修改组件为动态获取Signal

如果允许调整组件代码,将mySignal改为getter,每次访问都会从服务中获取最新的Signal引用:

// 组件代码修改
export class MyComponent {
  get mySignal(): Signal<any> {
    return this.myService.mySignal;
  }

  constructor(private myService: MyService) {}
}

此时测试用例只需更新服务属性后触发变更检测即可:

it('should have the extra property', () => {
  spyOnProperty(myServiceSpy, 'mySignal', 'get')
    .and.returnValue(signal(mockSignalValueWithExtraProperty));

  fixture.detectChanges();

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

额外注意事项

  • 访问Signal的值必须调用()(如component.mySignal()),直接访问属性会获取到Signal对象本身而非其值,这是原测试代码中的核心错误之一。
  • 确保beforeEach中定义的属性开启了configurable: true,否则无法在测试用例中重新定义。

内容的提问来源于stack exchange,提问作者Ashley Slaney

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.01 12:29:53