如何编写Angular AuthService单元测试并解决超时等相关问题
AuthService单元测试问题解决与完整测试指南
一、超时错误的原因与修复
你的测试超时是两个核心问题导致的:
- 使用
done回调但未在subscribe回调内调用done(),Jasmine始终等待异步完成信号; mokcReq.flush传递的是messageResponse.message(字符串),但服务的register方法期望返回MessageResponse对象,导致subscribe内的断言逻辑未正确触发,进一步卡住测试流程。
修复后的register测试代码:
it('should register user successfully', (done) => { const body: RegisterData = { email: 'test1@mail.com', firstName: 'test', lastName: 'test', password: '12345678', }; const messageResponse: MessageResponse = { message: 'User registered successfully', }; service.register(body).subscribe((response) => { expect(response).toEqual(messageResponse); // 更精准的对象断言 done(); // 必须调用done通知Jasmine异步流程结束 }); const mockReq = testingController.expectOne( `${environment.apiURL}/${EndPoints.auth}/${EndPoints.register}` ); expect(mockReq.request.method).toBe('POST'); // 额外断言请求方法正确性 mockReq.flush(messageResponse); // 传递完整的响应对象,而非字符串 testingController.verify(); });
二、关于数据库操作的疑问
完全不用担心测试会在真实数据库创建用户:
- 你使用的
HttpClientTestingModule会拦截所有HTTP请求,不会向真实后端发送请求,所有请求都是在内存中模拟完成的; - 测试里的
flush只是模拟后端返回的响应数据,不会触发任何真实的数据库写入操作。
三、其他方法的测试示例
1. login方法测试
it('should login user successfully', (done) => { const body: LoginData = { email: 'test@mail.com', password: '12345678', }; const mockUser: ResponseUser = { id: 1, email: 'test@mail.com', firstName: 'test', lastName: 'test', }; service.login(body).subscribe((user) => { expect(user).toEqual(mockUser); done(); }); const mockReq = testingController.expectOne( `${environment.apiURL}/${EndPoints.auth}/${EndPoints.login}` ); expect(mockReq.request.method).toBe('POST'); expect(mockReq.request.withCredentials).toBe(true); // 断言请求的withCredentials配置 mockReq.flush(mockUser); testingController.verify(); });
2. logout方法测试
it('should logout user successfully', (done) => { const mockResponse: MessageResponse = { message: 'Logged out successfully', }; service.logout().subscribe((response) => { expect(response).toEqual(mockResponse); done(); }); const mockReq = testingController.expectOne( `${environment.apiURL}/${EndPoints.auth}/${EndPoints.logout}` ); expect(mockReq.request.method).toBe('GET'); expect(mockReq.request.withCredentials).toBe(true); mockReq.flush(mockResponse); testingController.verify(); });
3. autoLogin方法测试
it('should auto-login user successfully', (done) => { const mockUser: ResponseUser = { id: 1, email: 'test@mail.com', firstName: 'test', lastName: 'test', }; service.autoLogin().subscribe((user) => { expect(user).toEqual(mockUser); done(); }); const mockReq = testingController.expectOne( `${environment.apiURL}/${EndPoints.auth}/${EndPoints.autoLogin}` ); expect(mockReq.request.method).toBe('GET'); expect(mockReq.request.withCredentials).toBe(true); mockReq.flush(mockUser); testingController.verify(); });
4. loggedUser$(NgRx Store)测试
需要模拟NgRx Store的状态返回,使用provideMockStore工具:
import { provideMockStore, MockStore } from '@ngrx/store/testing'; import { selectIsUserLogged } from './path-to-your-selectors'; // 替换为你的实际选择器路径 // 修改beforeEach中的TestBed配置 beforeEach(() => { TestBed.configureTestingModule({ imports: [HttpClientTestingModule], providers: [ AuthService, provideMockStore({ initialState: {}, selectors: [ { selector: selectIsUserLogged, value: false }, // 初始模拟未登录状态 ], }), ], }); service = TestBed.inject(AuthService); testingController = TestBed.inject(HttpTestingController); const store: MockStore = TestBed.inject(MockStore); }); it('should emit correct logged state from store', (done) => { // 模拟store返回已登录状态 store.overrideSelector(selectIsUserLogged, true); store.refreshState(); // 通知订阅者状态更新 service.loggedUser$.subscribe((isLogged) => { expect(isLogged).toBe(true); done(); }); });
内容的提问来源于stack exchange,提问作者Michał
相关产品推荐
相关产品推荐

