Angular 9中BehaviorSubject相关服务与组件单元测试编写及报错解决求助
Angular 9 单元测试解决方案
错误原因说明
currentUser是UserService的公开只读Observable属性,不是方法。jasmine.createSpyObj默认仅生成方法的间谍对象,如果你之前将currentUser作为方法名传入createSpyObj的第二个参数,就会出现「currentUser不是函数」的报错,正确做法是通过createSpyObj的第三个参数声明要模拟的属性。
1. UserService 单元测试代码(user.service.spec.ts)
import { TestBed } from '@angular/core/testing'; import { UserService } from './user.service'; import { User } from './user.model'; describe('UserService', () => { let service: UserService; beforeEach(() => { TestBed.configureTestingModule({}); service = TestBed.inject(UserService); }); it('should be created', () => { expect(service).toBeTruthy(); }); it('setCurrentUser should update currentUser observable', (done) => { const testUser: User = { username: 'testUser', id: 1 }; // 订阅currentUser验证返回值 service.currentUser.subscribe(user => { if (user.username === testUser.username) { expect(user).toEqual(testUser); done(); } }); service.setCurrentUser(testUser); }); });
2. HelloComponent 单元测试代码(hello.component.spec.ts)
这里采用模拟UserService的方案,完全隔离外部依赖,符合单元测试规范:
import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; import { HelloComponent } from './hello.component'; import { UserService } from './user.service'; import { BehaviorSubject } from 'rxjs'; import { User } from './user.model'; import { By } from '@angular/platform-browser'; describe('HelloComponent', () => { let component: HelloComponent; let fixture: ComponentFixture<HelloComponent>; let mockUserSubject: BehaviorSubject<User>; let mockUserService: jasmine.SpyObj<UserService>; beforeEach(async () => { // 初始化模拟用户流 mockUserSubject = new BehaviorSubject<User>({} as User); // 正确创建带属性的间谍对象:第二个参数是要模拟的方法,第三个参数是要模拟的属性 mockUserService = jasmine.createSpyObj( 'UserService', ['setCurrentUser'], { currentUser: mockUserSubject.asObservable() } ); await TestBed.configureTestingModule({ declarations: [ HelloComponent ], providers: [ { provide: UserService, useValue: mockUserService } ] }) .compileComponents(); }); beforeEach(() => { fixture = TestBed.createComponent(HelloComponent); component = fixture.componentInstance; fixture.detectChanges(); }); it('should create', () => { expect(component).toBeTruthy(); }); it('初始状态无用户时,username为空,模板不显示问候语', () => { expect(component.username).toBeUndefined(); const h1Element = fixture.debugElement.query(By.css('h1')); expect(h1Element).toBeFalsy(); }); it('服务推送新用户时,username更新,模板显示对应问候语', fakeAsync(() => { const testUser: User = { username: 'jack', id: 2 }; // 推送新用户 mockUserSubject.next(testUser); tick(); fixture.detectChanges(); // 验证组件属性 expect(component.username).toBe('jack'); // 验证模板渲染 const h1Element = fixture.debugElement.query(By.css('h1')); expect(h1Element.nativeElement.textContent).toContain('Hello jack!'); })); it('组件销毁时自动取消订阅', () => { // 给订阅的unsubscribe方法加间谍 const unsubscribeSpy = spyOn(component['userServiceSubscription'] as any, 'unsubscribe'); fixture.destroy(); expect(unsubscribeSpy).toHaveBeenCalled(); }); });
内容的提问来源于stack exchange,提问作者sandeep kottapalli
相关产品推荐
相关产品推荐

