Angular Jest测试报Cannot read property 'login' of undefined问题求助
问题排查与修复方案
错误原因列表
- 服务注入时机错误:你在异步配置TestBed的流程中,没有等待TestBed编译完成就调用
injectMocked注入服务,导致authenticationService为undefined,这是本次报错的直接原因。
- 服务注入时机错误:你在异步配置TestBed的流程中,没有等待TestBed编译完成就调用
- 依赖导入缺失:组件使用了Angular响应式表单,但测试模块仅导入了模板驱动表单需要的
FormsModule,缺少响应式表单依赖ReactiveFormsModule,会导致表单初始化失败。
- 依赖导入缺失:组件使用了Angular响应式表单,但测试模块仅导入了模板驱动表单需要的
- Mock服务返回值不符合要求:
MockAuthenticationService的login方法没有返回Observable对象,组件调用login后执行订阅操作会抛出异常。
- Mock服务返回值不符合要求:
- 测试逻辑不成立:测试用例没有触发登录提交操作,直接断言
login被调用1次,逻辑本身不成立。
- 测试逻辑不成立:测试用例没有触发登录提交操作,直接断言
- 冗余代码:定义了未使用的
authServiceMock变量,可直接删除。
- 冗余代码:定义了未使用的
修正后的测试代码
import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { NO_ERRORS_SCHEMA } from '@angular/core'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { of } from 'rxjs'; import { LoginComponent } from './login.component'; import {AuthenticationService} from '../../../services/authentication.service'; import { createComponent, Page, injectMocked } from '@webex/common/test-utils'; const MockAuthenticationService = { // 让mock的login返回空的Observable,符合接口要求 login: jest.fn(() => of({})), }; class LoginPage extends Page<LoginComponent> { constructor(fixture: ComponentFixture<LoginComponent>) { super(fixture); } // 封装页面元素操作方法,方便测试调用 get usernameInput() { return this.query<HTMLInputElement>('#username'); } get passwordInput() { return this.query<HTMLInputElement>('#password'); } get submitBtn() { return this.query<HTMLButtonElement>('button[type="submit"]'); } } describe('LoginComponent', () => { let component: LoginComponent; let fixture: ComponentFixture<LoginComponent>; let page: LoginPage; let authenticationService: jest.Mocked<AuthenticationService>; beforeEach(async(() => { TestBed.configureTestingModule({ declarations: [ LoginComponent ], providers: [ { provide: AuthenticationService, useValue: MockAuthenticationService, }, ], imports: [ FormsModule, // 新增响应式表单依赖 ReactiveFormsModule ], schemas: [NO_ERRORS_SCHEMA], }) // 等待TestBed编译完成 .compileComponents() .then(() => { // 编译完成后再注入服务 authenticationService = injectMocked(AuthenticationService); }); })); beforeEach(() => { ({ fixture, component, page } = createComponent(LoginComponent, LoginPage)); fixture.detectChanges(); }); it('should call login method when submit valid form', () => { // 1. 填充有效表单值 page.usernameInput.value = 'testUser'; page.usernameInput.dispatchEvent(new Event('input')); page.passwordInput.value = 'testPass'; page.passwordInput.dispatchEvent(new Event('input')); fixture.detectChanges(); // 2. 触发提交按钮点击 page.submitBtn.click(); fixture.detectChanges(); // 3. 断言login方法被调用,参数正确 expect(authenticationService.login).toHaveBeenCalledTimes(1); expect(authenticationService.login).toHaveBeenCalledWith('testUser', 'testPass'); }); });
内容的提问来源于stack exchange,提问作者pranami
相关产品推荐
相关产品推荐

