使用Jasmine修改UserIdService返回值无效的问题排查(涉及Angular组件ngOnInit生命周期)
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.
Modify your second
beforeEachblock:beforeEach(() => { fixture = TestBed.createComponent(UserpointsComponent); component = fixture.componentInstance; // Remove fixture.detectChanges() here—we'll call it per test });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); });For other tests needing the default behavior:
Just callfixture.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 thaninnerHTMLbecause it handles any unexpected whitespace in your template.
内容的提问来源于stack exchange,提问作者Joel

