首页 > 解决方案 > 如何在单元测试中将消息从 Observable 发送到 Observer

问题描述

我有一个UserManagementService暴露了一个 Observable 的 a BehaviourSubject

this.userSignInState$ = this.signInStateSubject.asObservable();

userSignInState$是公开的,signInStateSubject而是私有的。

我订阅了userSignInState$一个nav组件。

constructor(public userManagementService: UserManagementService, private fb:FormBuilder, private helper:HelperService) {
    this.userSignInStateSubscription = this.userManagementService.userSignInState$.subscribe(
          (result:Result)=> {
            console.log("In nav - result from user signin state ",result);
            let subscribed:UserSigninState = result.additionalInfo;
            console.log("new user signin state received:", subscribed);
            this.userLoggedIn = subscribed.isSignedIn;
            if(subscribed.isSignedIn && subscribed['additional-info'] !== ''){
              this.profile = JSON.parse(subscribed['additional-info']) as UserProfileAPI
            }
            if(!subscribed.isSignedIn && subscribed['additional-info'] !== ''){
            //  let error:ServerResponseAPI = JSON.parse(subscribed['additional-info']) as ServerResponseAPI
              //let errorMessage:string = this.helper.userFriendlyErrorMessage(error);
              this.navEvent.emit(new NavContext(subscribed['additional-info']));
            }
          },
        (error:ServerResponseAPI)=>{
            console.log("got error from the Observable: ",error);

           let errorMessage:string = this.helper.userFriendlyErrorMessage(error);
           this.navEvent.emit(new NavContext(errorMessage));
    //       this.userloggedIn =false;
      },
      ()=>{ //observable complete
        console.log("observable completed")
        //this.userloggedIn =false;
      });
  }

我想进行单元测试nav。作为规范的一部分,我想使用nextsignInStateSubject检查是否接收到nav发送的消息next。但我不能这样做,因为这signInStateSubject是私人的。如果我signInStateSubject公开,我可以这样做,但这听起来不对。这

我编写了以下规范,但只有在我signInStateSubject公开时才有效。

fit('should subscribe to user sign in state observable',()=>{
    let userManagementService = TestBed.get(UserManagementService);
    let navComponent:NavComponentComponent = component;
    console.log('component is ',navComponent);
    navComponent.userLoggedIn = false;
    let dummyUserProfile = new UserProfileAPI(new User('fn','ln','test@test.com'));
    userManagementService.signInStateSubject.next(new Result('success',(new UserSigninState(true,JSON.stringify(dummyUserProfile ))))); //THIS WORKS ONLY IF signInStateSubject IS PUBLIC
    expect(navComponent.userLoggedIn).toBe(true)
  });

标签: jasminerxjsangular6angular-test

解决方案


当我在做单元测试时,我应该嘲笑UserManagementService. 去做这个。我创建了一个模拟类,其中包含使用的方法和UserManagementService属性nav

class MockUserManagementService {
  signInStateSubject:BehaviorSubject<Result> ;
  userSignInState$:Observable<Result>;//naming convention for Streams has $ in the end.

  constructor(){
    this.signInStateSubject = new BehaviorSubject<Result>(new Result('initial',""));//create Observable. Other components can subscribe to it now to get notifications/values
    this.userSignInState$ = this.signInStateSubject.asObservable();

  }
  getUserProfile(userId: UserID){
    console.log('mocked getUserProfile called');
  }
}

然后我provide用嘲笑的班级代替了真实的班级UserManagementClass

beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ ...],
      imports:[...],
      providers:[...,
        {provide: UserManagementService,useClass:MockUserManagementService},//mock user management service
    })
    .compileComponents();
  }));

规格与之前一样。但这一次,我不需要UserManagementService公开真实的任何属性,因为服务是模拟的!

it('should subscribe to user sign in state observable',()=>{
    let navComponent:NavComponentComponent = component;
    let userManagementService = TestBed.get(UserManagementService);//component.userManagementService;
    console.log('mock service is ',userManagementService);
    console.log('subject is ',userManagementService.signInStateSubject);
    console.log('observable is ',userManagementService.userSignInState$);
    console.log('nav is ',navComponent);
    expect(navComponent.userSignInStateSubscription.closed).toBe(false);
  });

  it('should should set user as signedIn for successful signin attempt',()=>{
    let navComponent:NavComponentComponent = component;
    /*The idea of unit test is that UserManagementService might not be available yet
    so mock UserManagementService
     */
    let userManagementService = TestBed.get(UserManagementService);//component.userManagementService;
    console.log('got mocked user management service',userManagementService);
    //component.subscribeToUserSignInState();
    console.log('component is ',navComponent);
    navComponent.userLoggedIn = false;
    let dummyUserProfile = new UserProfileAPI(new User('manu','chadha','test@test.com'));
    console.log('dummy profile is ',dummyUserProfile);
    console.log('dummy profile is JSON stringify',JSON.stringify(dummyUserProfile));
    let userSignInState = new UserSigninState(true,JSON.stringify(dummyUserProfile));
    console.log('user signin state ',userSignInState);
    userManagementService.signInStateSubject.next(new Result('success',userSignInState));
    //userManagementService.signInStateSubject.next(new Result('success1',(new UserSigninState(true,JSON.stringify(dummyUserProfile))).toString));
    expect(navComponent.userLoggedIn).toBe(true)
  });

  it('should get user\'s profile',()=>{
    let userManagementService = TestBed.get(UserManagementService);
    let navComponent:NavComponentComponent = component;
    console.log('component is ',navComponent);
    navComponent.profile = new UserProfileAPI(new User("fn","ln","test@test.com"));
    spyOn(userManagementService,'getUserProfile');
    component.onProfileClick();
    let userId = new UserID(navComponent.profile['external-profile'].email);
    expect(userManagementService.getUserProfile).toHaveBeenCalledWith(userId);
  });

推荐阅读