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

如何用Karma/Angular/Jasmine测试Post订阅?测试覆盖率不达标

Angular登录逻辑单元测试覆盖率优化方案

你当前的登录逻辑包含三个未完全覆盖的代码分支:next回调中user.sc === true、user.sc === false,以及error回调,现有测试仅覆盖了第一个分支,这是覆盖率不达标的核心原因。以下是完善后的测试用例,覆盖所有分支并满足覆盖率要求:

1. 测试前置准备

在测试文件的beforeEach中完成依赖注入与必要Mock,避免测试污染:

let component: LoginComponent;
let fixture: ComponentFixture<LoginComponent>;
let apiService: ApiServicesService;
let toastrService: ToastrService;
const routerSpy = jasmine.createSpyObj('Router', ['navigate']);

beforeEach(async () => {
  await TestBed.configureTestingModule({
    declarations: [LoginComponent],
    providers: [
      ApiServicesService,
      { provide: ToastrService, useValue: jasmine.createSpyObj('ToastrService', ['success', 'error']) },
      { provide: Router, useValue: routerSpy }
    ]
  }).compileComponents();

  fixture = TestBed.createComponent(LoginComponent);
  component = fixture.componentInstance;
  apiService = TestBed.inject(ApiServicesService);
  toastrService = TestBed.inject(ToastrService);
  
  // Mock localStorage操作
  spyOn(localStorage, 'setItem');
  spyOn(localStorage, 'getItem');
});

2. 测试登录成功分支(user.sc === true)

优化原有测试,去除冗余断言,验证所有关键行为:

it('should handle successful login when user.sc is true', () => {
  const successResponse = {
    sc: true,
    result: {
      first_name: 'Test',
      last_name: 'User',
      token: 'test-token-123'
    }
  };
  spyOn(apiService, 'post').and.returnValue(of(successResponse));

  component.onSubmit();

  expect(apiService.post).toHaveBeenCalledWith('/login', component.loginForm.value);
  expect(localStorage.setItem).toHaveBeenCalledWith('firstName', successResponse.result.first_name);
  expect(localStorage.setItem).toHaveBeenCalledWith('lastName', successResponse.result.last_name);
  expect(localStorage.setItem).toHaveBeenCalledWith('token', successResponse.result.token);
  expect(component.loading).toBe(false);
  expect(toastrService.success).toHaveBeenCalledWith('Login successful');
  expect(routerSpy.navigate).toHaveBeenCalledWith(['/under-construction']);
  expect(component.isSubmitted).toBe(false);
  expect(component.loginForm.pristine).toBe(true);
});

3. 测试登录失败分支(user.sc === false)

新增该分支测试,验证错误提示与状态重置:

it('should handle failed login when user.sc is false', () => {
  const failResponse = {
    sc: false,
    error: {
      msg: 'Invalid username or password'
    }
  };
  spyOn(apiService, 'post').and.returnValue(of(failResponse));

  component.onSubmit();

  expect(apiService.post).toHaveBeenCalledWith('/login', component.loginForm.value);
  expect(component.loading).toBe(false);
  expect(toastrService.error).toHaveBeenCalledWith(failResponse.error.msg);
  expect(component.isSubmitted).toBe(false);
  expect(localStorage.setItem).not.toHaveBeenCalled();
  expect(routerSpy.navigate).not.toHaveBeenCalled();
});

4. 测试API请求错误分支

新增错误回调测试,验证服务端异常处理:

it('should handle API request error', () => {
  const apiError = {
    error: {
      error: {
        msg: 'Server connection failed'
      }
    }
  };
  spyOn(apiService, 'post').and.returnValue(throwError(() => apiError));

  component.onSubmit();

  expect(apiService.post).toHaveBeenCalledWith('/login', component.loginForm.value);
  expect(component.loading).toBe(false);
  expect(toastrService.error).toHaveBeenCalledWith(apiError.error.error.msg);
  expect(localStorage.setItem).not.toHaveBeenCalled();
  expect(routerSpy.navigate).not.toHaveBeenCalled();
});

以上用例覆盖了登录逻辑的所有代码路径,验证了每个分支下的关键行为(存储操作、状态变更、提示调用、路由跳转等),执行ng test --code-coverage即可达标。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 16:15:54