Angular单元测试如何Mock嵌套方法?为何出现未定义错误?
问题原因及解决方案
核心错误原因
你遇到的Cannot read properties of undefined (reading 'search')是因为组件构造函数执行时机早于mock返回值的设置:
当调用TestBed.createComponent(LoginPageComponent)时,组件实例会被创建,构造函数立即执行this.angularLocation = locationHelperService.getLocation()。但此时你还没给mockLocationService.getLocation设置返回值,jasmine spy默认返回undefined,导致this.angularLocation变成undefined,后续调用redirectToDashboard时自然找不到search()方法。
另外还有个小问题:你mock的search()方法返回{ path: { ref: '/other-page' } },但组件里判断的是path.ref,这会导致path.ref始终为undefined,永远走else分支跳转到/dashboard,和你预期的测试逻辑不符。
修正后的测试代码
const mockLocationService = jasmine.createSpyObj('LocationHelperService', ['getLocation']); // 先创建mock的location对象,包含需要的方法 const mockAngularLocation = { search: jasmine.createSpy().and.returnValue({ ref: '/other-page' }), url: jasmine.createSpy(), path: jasmine.createSpy() }; beforeEach(() => { TestBed.configureTestingModule({ // 其他配置 providers: [ { provide: LocationHelperService, useValue: mockLocationService }, ], }).compileComponents(); // 关键:先设置getLocation的返回值,再创建组件 mockLocationService.getLocation.and.returnValue(mockAngularLocation); fixture = TestBed.createComponent(LoginPageComponent); component = fixture.componentInstance; }); it('should redirect to the ref path when ref exists', waitForAsync(() => { fixture.detectChanges(); const loginButton = fixture.debugElement.nativeElement.querySelector('#login-button'); loginButton.click(); fixture.detectChanges(); fixture.whenStable().then(() => { // 验证search方法被调用 expect(mockAngularLocation.search).toHaveBeenCalled(); // 验证跳转到了指定的ref路径 expect(mockAngularLocation.url).toHaveBeenCalledWith('/other-page'); // 如果测试无ref的情况,只需修改search的返回值为{},然后断言path被调用 }); }));
关键修改点
- 调整mock设置顺序:在
createComponent之前就给mockLocationService.getLocation设置返回值,确保组件构造函数执行时能拿到正确的mock对象。 - 修正返回结构:让
search()直接返回{ ref: '/other-page' },匹配组件里path.ref的判断逻辑。 - 给mock方法添加spy:对
url和path方法也创建spy,方便后续验证跳转行为是否符合预期。
内容的提问来源于stack exchange,提问作者zen zen
相关产品推荐
相关产品推荐

