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

使用Jasmine修改UserIdService返回值无效的问题排查(涉及Angular组件ngOnInit生命周期)

Fixing the Mock Service Return Value Issue in Angular Component Testing

Let's break down why your getUserId spy isn't taking effect and how to fix it:

Root Cause

The problem is timing: In your beforeEach block, you call fixture.detectChanges() immediately after creating the component. This triggers the component's ngOnInit lifecycle hook, which runs initCustomerPoints() and sets up the observable chain for customerPoints$—using the original MockUserIdService that returns "123". When you try to spy on getUserId later in your test case, the observable has already subscribed to the initial value, so your new spy doesn't affect it.

Solution 1: Delay fixture.detectChanges() Until After Spy Setup

The cleanest fix is to hold off on triggering ngOnInit until you've set up all necessary spies for each test case.

  1. Modify your second beforeEach block:

    beforeEach(() => {
      fixture = TestBed.createComponent(UserpointsComponent);
      component = fixture.componentInstance;
      // Remove fixture.detectChanges() here—we'll call it per test
    });
    
  2. Update your test case:

    it("test no points returned", () => {
      // Set up the spy FIRST
      spyOn(userIdService, 'getUserId').and.returnValue(of(OCC_USER_ID_ANONYMOUS));
      
      // Now trigger ngOnInit and render the component
      fixture.detectChanges();
      
      const paragraphElement = fixture.debugElement.query(By.css('p')).nativeElement;
      // Anonymous user returns 0 points, so update your expectation
      const expectedText = `${FOOTER_TEXT} 0`;
      expect(paragraphElement.textContent.trim()).toBe(expectedText);
    });
    
  3. For other tests needing the default behavior:
    Just call fixture.detectChanges() without overriding the spy:

    it("test points returned for logged-in user", () => {
      fixture.detectChanges();
      
      const paragraphElement = fixture.debugElement.query(By.css('p')).nativeElement;
      const expectedText = `${FOOTER_TEXT} ${POINTS}`;
      expect(paragraphElement.textContent.trim()).toBe(expectedText);
    });
    

Solution 2: Re-Initialize the Observable Chain

If you prefer to keep fixture.detectChanges() in your beforeEach, you can re-run the initCustomerPoints() method after setting the spy to create a new observable chain that uses the updated spy:

it("test no points returned", () => {
  spyOn(userIdService, 'getUserId').and.returnValue(of(OCC_USER_ID_ANONYMOUS));
  
  // Re-set up the observable chain with the new spy
  component.initCustomerPoints();
  
  // Update the view to reflect the new data
  fixture.detectChanges();
  
  const paragraphElement = fixture.debugElement.query(By.css('p')).nativeElement;
  const expectedText = `${FOOTER_TEXT} 0`;
  expect(paragraphElement.textContent.trim()).toBe(expectedText);
});

Key Notes

  • Why the expectation was wrong: When the user is anonymous, your component returns ZERO_CUSTOMER_POINTS (points: 0), so the template will display ${FOOTER_TEXT} 0, not just ${FOOTER_TEXT} .
  • Using textContent.trim(): This is more reliable than innerHTML because it handles any unexpected whitespace in your template.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.27 19:33:12