Вычисленный сигнал Angular в компоненте не обновляется при изменении служебного сигнала

Почему expect(component['numberHogeNames']()).toBe(3) не проходит мой тест Angular?

Вот упрощенная версия моего кода:

// hoge-service.ts
interface HogeData {
  id: string;
  name: string;
}

export class HogeService {
  private dataSrc = signal<HogeData[]>([
    { id: '1', name: 'kage' },
    { id: '2', name: 'sage' },
    { id: '3', name: 'tage' },
    { id: '4', name: 'nage' },
    { id: '5', name: 'mage' },
    { id: '6', name: 'yage' },
    ...
  ]);

  numberHogeNames = computed<number>(() => {
    return this.dataSrc().filter((item) => item.name === 'hoge').length;
  });
}
// hoge-component.ts
@Component({
  standalone: true,
  providers: [HogeService],
  template: `<div class = "num">{{ numberHogeNames() }}</div>`,
  encapsulation: ViewEncapsulation.Emulated,
  changeDetection: ChangeDetectionStrategy.OnPush,
  ...
})
export class HogeComponent {
  private hogeService = inject(HogeService);

  protected numberHogeNames = computed<number>(() => {
    return this.hogeService.numberHogeNames();
  });
}
// hoge-component.spec.ts
describe('Hoge Test', () => {
  let component: HogeComponent;
  let fixture: ComponentFixture<HogeComponent>;
  let hogeService: HogeService;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      imports: [HogeComponent],
      providers: [HogeService],
    }).compileComponents();

    fixture = TestBed.createComponent(HogeComponent);
    component = fixture.componentInstance;
    hogeService = TestBed.inject(HogeService);
  });

  it('Case 01', async () => {
    hogeService['dataSrc'].update((current) => {
      current.forEach((item, index) => {
        if (index < 3) item.name = 'hoge';
      });
      return [...current];
    });

    // Success
    expect(hogeService.numberHogeNames()).toBe(3);

    fixture.detectChanges();

    // Failed (Expected 0 to be 3.)
    expect(component['numberHogeNames']()).toBe(3);
  })
});

Несмотря на провал теста, компонент работает корректно при использовании в реальном приложении. Значение numberHogeNames на экране обновляется, как и ожидалось, при изменении dataSrc.

Я попытался изменить частный сигнал dataSrc в HogeService, используя update(), чтобы изменить свойства name некоторых элементов на hoge. После обновления dataSrc я ожидал, что hogeService.numberHogeNames() и component['numberHogeNames']() вернутся 3, поскольку есть три элемента с именем hoge.

В тесте hogeService.numberHogeNames() корректно обновляется до 3, что является ожидаемым поведением. Однако component['numberHogeNames']() по-прежнему возвращает старое значение и не обновляется должным образом.

Я пробовал использовать fixture.detectChanges() и ждать небольшой задержки с помощью setTimeout, чтобы посмотреть, обновится ли вычисленный сигнал компонента, но эти попытки не решили проблему.

Я ожидаю, что вычисленный сигнал компонента (numberHogeNames) будет отражать изменения сигнала службы (dataSrc) так же, как это происходит в реальном приложении, где все работает так, как задумано.

🤔 А знаете ли вы, что...
Angular обладает многими инструментами и рекомендациями по безопасности приложений.


50
1

Ответ:

Решено

Вам необходимо сослаться на сервис, который внедрен в компонент (т.е. component.hogeService):

    component.hogeService['dataSrc'].update((current) => {
      current.forEach((item, index) => {
        if (index < 3) item.name = 'hoge';
      });
      return [...current];
    });

Потом позже:

expect(component.hogeService.numberHogeNames()).toBe(3);

Тест после этих изменений должен пройти.


Интересные вопросы для изучения

Angular 18: использовать входной сигнал в суперклассе компонентовПредупреждение Angular Calendar: NG0956 — отслеживание по идентификатору приводило к повторному созданию всей коллекцииИспользование синтаксиса @ifКак объединить строку и массив в строковой интерполяции ngfor в AngularКаков наилучший способ совместного использования переменных SCSS между библиотеками и приложениями в монорепо Angular Nx?Двусторонняя привязка Angular нарушена в производственной сборке - TypeError «st не является функцией»Как динамически устанавливать или снимать флажок на основе возвращаемого логического значения из функции в AngularПочему мои значки материалов Angular отличаются от значков Google Fonts?Tailwind CSS IntelliSense больше не предлагает классы CSS в монорепозитории NXRxJS: `refCount: false` в ShareReplay в сервисе с ограниченной областью действия: будет ли он завершен и будет ли собран мусор при уничтожении сервиса?