【问题标题】:How to mock Angular BehaviourSubject when Unit Testing单元测试时如何模拟 Angular BehaviorSubject
【发布时间】:2020-12-03 11:44:12
【问题描述】:

我想在收到BehaviourSubject 更新或状态更改时测试我的 AppComponent,所以我的目标是在我的AppComponent 中模拟authService.authEvent:

export class AppComponent implements OnInit, OnDestroy {
  
  user: any
  loggedIn = false
  
  constructor(
    private router: Router,
    private authService: AuthService
  ) {}

  ngOnInit(): void {
    this.authService.authEvent.subscribe((user) => {
      if (user) {
        this.setLoggedIn(user)
      } else {
        this.setLoggedOut()
      }
    });
  }

  setLoggedIn(user: any) {
    this.user = user
    this.loggedIn = true

  }

我的 AuthService 如下(authEvent BehaviourSubject 是我想模拟的):

@Injectable()
export class AuthService {

  authEvent = new BehaviorSubject<any>(null);
  user: any = {}
  
  constructor(

  ) {}


  setUser(user: any) {
    console.log('setUser');
    this.user = { ...user }
    sessionStorage.setItem('app-user', JSON.stringify(this.user))
    this.authEvent.next(this.user)
  }

我的单元测试,我知道这不是模拟 BehaviourSubject 的正确方法,但我还没有找到任何好的示例:


import { BehaviourSubject } from 'rxjs';

export class MockAuthService {

    authEvent = new BehaviourSubject<any>();

    setUser() {
    //mock implementation
    }
}


describe('AppComponent', () => {
  let component: AppComponent;
  let fixture: ComponentFixture<AppComponent>;
  let mockAuthService;
  
  beforeEach(async(() => {

    mockAuthService = jasmine.createSpyObj(['setUser']);

    TestBed.configureTestingModule({
      imports: [
        RouterTestingModule
      ],
      declarations: [
        AppComponent
      ],
      providers: [
        { provide: AuthService, useClass: MockAuthService }
      ],
      schemas: [
        CUSTOM_ELEMENTS_SCHEMA
      ]
    }).compileComponents();

    
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(AppComponent);
    component = fixture.componentInstance;
  })

  it('should create the app', () => {
   
    const app = fixture.componentInstance;
    
    expect(app).toBeTruthy();
  });

 
  describe('ngOnInit', () => {
    it('should call setLoggedIn user if user received from authEvent', () => {
      const mockAuthService = TestBed.get(AuthService);
      const userValue = {userName: 'myUser'};
      const spy = spyOn(mockAuthService, 'authEvent').and.returnValue(of(userValue));
      const spyLoggedOut = spyOn(component, 'setLoggedIn').and.callThrough();

      fixture.detectChanges();

      expect(setLoggedIn).toHaveBeenCalled();
      
    });

  });
  
});


非常感谢您的帮助!

【问题讨论】:

    标签: angular unit-testing mocking


    【解决方案1】:

    您可以通过三种方式模拟服务:

    1. 像你一样提供一个 Mock 实体
    2. 在组件中模拟 Service 的实例。
    3. 使用 TestBed.get() 获取 Service 实例并模拟该 Service 实例

    独立地,您选择以哪种方式模拟服务,您需要以fakeAsync 执行单元测试,因为您正在使用 Observable 哪些响应不是即时的。

    1) 像你一样提供一个 Mock 实体

    import { Subject} from 'rxjs';
    
    
    describe('AppComponent', () => {
      let component: AppComponent;
      let fixture: ComponentFixture<AppComponent>;
      const mySubject = new Subject();
      
      beforeEach(async(() => {
    
        TestBed.configureTestingModule({
          imports: [
            RouterTestingModule
          ],
          declarations: [
            AppComponent
          ],
          providers: [
            { provide: AuthService, useValue: { authEven: mySubject.asObservable()} }
          ],
          schemas: [
            CUSTOM_ELEMENTS_SCHEMA
          ]
        }).compileComponents();
    
        
      }));
    
      beforeEach(() => {
        fixture = TestBed.createComponent(AppComponent);
        component = fixture.componentInstance;
      })
    
      it('should create the app', () => {
        const app = fixture.componentInstance;
        expect(app).toBeTruthy();
      });
    
     
      describe('ngOnInit', () => {
        it('should call setLoggedIn user if user received from authEvent', fakeAsync(() => {
          const spyLoggedOut = spyOn(component, 'setLoggedIn');;
          const userValue = {userName: 'myUser'};
          component.ngOnInit();
          mySubject.next(userValue);
          tick();
          fixture.detectChanges();
          expect(setLoggedIn).toHaveBeenCalled();
          flush();
          
        }));
    
      });
      
    });
    

    2) 在组件中模拟Service的实例。

    
    describe('AppComponent', () => {
      let component: AppComponent;
      let fixture: ComponentFixture<AppComponent>;
      
      beforeEach(async(() => {
    
        TestBed.configureTestingModule({
          imports: [
            RouterTestingModule
          ],
          declarations: [
            AppComponent
          ],
          providers: [
            { provide: AuthService }
          ],
          schemas: [
            CUSTOM_ELEMENTS_SCHEMA
          ]
        }).compileComponents();
    
        
      }));
    
      beforeEach(() => {
        fixture = TestBed.createComponent(AppComponent);
        component = fixture.componentInstance;
      })
    
      it('should create the app', () => {
        const app = fixture.componentInstance;
        expect(app).toBeTruthy();
      });
    
     
      describe('ngOnInit', () => {
        it('should call setLoggedIn user if user received from authEvent', fakeAsync(() => {
          const spyLoggedOut = spyOn(component, 'setLoggedIn');
          const userValue = {userName: 'myUser'};
          spyOn(component['authService'], 'authEvent').and.returnValue(of(userValue)); //
          component.ngOnInit();
          tick();
          fixture.detectChanges();
          expect(setLoggedIn).toHaveBeenCalled();
          flush();
          
        }));
    
      });
      
    });
    

    3) 使用 TestBed.get() 获取 Service 实例并模拟该 Service 实例

    
    describe('AppComponent', () => {
      let component: AppComponent;
      let fixture: ComponentFixture<AppComponent>;
      let authService: AuthService ;
      beforeEach(async(() => {
    
        TestBed.configureTestingModule({
          imports: [
            RouterTestingModule
          ],
          declarations: [
            AppComponent
          ],
          providers: [
            { provide: AuthService }
          ],
          schemas: [
            CUSTOM_ELEMENTS_SCHEMA
          ]
        }).compileComponents();
    
        
      }));
    
      beforeEach(() => {
        fixture = TestBed.createComponent(AppComponent);
        component = fixture.componentInstance;
        authService = TestBed.get(AuthService);
      })
    
      it('should create the app', () => {
        const app = fixture.componentInstance;
        expect(app).toBeTruthy();
      });
    
     
      describe('ngOnInit', () => {
        it('should call setLoggedIn user if user received from authEvent', fakeAsync(() => {
          const spyLoggedOut = spyOn(component, 'setLoggedIn');
          const userValue = {userName: 'myUser'};
          spyOn(authService, 'authEvent').and.returnValue(of(userValue)); //
          component.ngOnInit();
          tick();
          fixture.detectChanges();
          expect(setLoggedIn).toHaveBeenCalled();
          flush();
          
        }));
    
      });
      
    });
    

    我更喜欢2)在组件中模拟Service的实例。因为它的代码更少,更简单

    【讨论】:

      猜你喜欢
      • 2018-11-24
      • 2017-05-16
      • 1970-01-01
      • 1970-01-01
      • 2020-06-19
      • 2021-09-18
      • 1970-01-01
      • 2020-11-19
      • 1970-01-01
      相关资源
      最近更新 更多