【问题标题】:how to mock out an Observable? I have tried to mock it out three different ways如何模拟一个 Observable?我试图用三种不同的方式来模拟它
【发布时间】:2019-03-09 02:47:38
【问题描述】:

我已经编写了许多单元测试来测试 observables,但是由于某种原因,这个测试不像我编写的其他测试那样工作,但我不明白问题是什么。

我想要一个模拟 ChangePasswordFacade.successful$ 来返回一个返回 true 的 Observable。即使我以前使用过下面的模拟策略,它也不起作用。我尝试将 ChangePasswordFacade 注入测试。我尝试在 TestBed 提供程序的 useValue 中使用模拟成功 $ 函数。我尝试了下面的方法。它们都将 successful$ 视为 false。我怎样才能以不同的方式模拟它以返回 true?

import { ComponentFixture, TestBed } from '@angular/core/testing';
import { PasswordChangeModalComponent } from './password-change-modal.component';
import { ReactiveFormsModule } from '@angular/forms';
import { PasswordChangeFormComponent } from '../../..';
import { DefaultPopoverComponent } from '@bis-angular/shared-components/pattern-library';
import { Store, StoreModule } from '@ngrx/store';
import {
  ChangePasswordFacade,
  UserInformationFacade,
  changePasswordInitialState,
  CHANGEPASSWORD_FEATURE_KEY,
  changePasswordReducer
} from '@bis-angular/users';
import { configureTestSuite } from 'ng-bullet';
import { of } from 'rxjs';

describe('PasswordChangeModalComponent', () => {
  let component: PasswordChangeModalComponent;
  let fixture: ComponentFixture<PasswordChangeModalComponent>;

  const childDefaultPopoverComponent = jasmine.createSpyObj('DefaultPopoverComponent', ['hideModal', 'showModal']);
  const childPasswordChangeFormComponent = jasmine.createSpyObj('PasswordChangeFormComponent', ['setFormControlsToEmpty']);

  const changePasswordFacadeSpy = jasmine.createSpyObj('ChangePasswordFacade', ['resetState']);

  configureTestSuite(() => {
    TestBed.configureTestingModule({
      imports: [
        ReactiveFormsModule,
        StoreModule.forRoot({}),
        StoreModule.forFeature(CHANGEPASSWORD_FEATURE_KEY, changePasswordReducer, {
          initialState: changePasswordInitialState
        })
      ],
      declarations: [PasswordChangeFormComponent, DefaultPopoverComponent, PasswordChangeModalComponent],
      providers: [Store, UserInformationFacade, ChangePasswordFacade]
    });
  });

  beforeEach(() => {
    fixture = TestBed.createComponent(PasswordChangeModalComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
    component.defaultPopoverComponent = childDefaultPopoverComponent;

    component.watchSuccessful = { unsubscribe: () => {} };
    spyOn(component.watchSuccessful, 'unsubscribe');
    const service = TestBed.get(ChangePasswordFacade);
    spyOn(service, 'successful$').and.returnValue(of(true));
  });


  describe('showPasswordChangeModal function ', () => {
    it('should call showModal and then not hide if not successful  ', () => {
      spyOn(component, 'hidePasswordChangeModal');
      component.successful$.subscribe((successful: boolean) => {
        expect(component.hidePasswordChangeModal).toHaveBeenCalled();
        expect(changePasswordFacadeSpy.resetState).toHaveBeenCalled();
        expect(component.watchSuccessful.unsubscribe).toHaveBeenCalled();
      });
      component.showPasswordChangeModal();
      expect(childDefaultPopoverComponent.showModal).toHaveBeenCalled();
    });
  });


});
import { Injectable } from '@angular/core';
import { select, Store } from '@ngrx/store';
import { ChangePasswordPartialState } from './change-password.reducer';
import { changePasswordQuery } from './change-password.selectors';
import { ChangePassword, ChangePasswordResetState } from './change-password.actions';
import { NewPassword } from '@bis-angular/users';

@Injectable()
export class ChangePasswordFacade {
  successful$ = this.store.pipe(select(changePasswordQuery.getSuccessful));

  initiated$ = this.store.pipe(select(changePasswordQuery.getInitiated));

  constructor(private store: Store<ChangePasswordPartialState>) {}

  changePassword(newPassword: NewPassword, userId: string) {
    this.store.dispatch(new ChangePassword(newPassword, userId));
  }

  resetState() {
    this.store.dispatch(new ChangePasswordResetState());
  }
}

【问题讨论】:

  • 您是否有机会在 @Component 装饰器的 providers 数组中为 PasswordChangeModalComponent 的类定义声明 ChangePasswordFacade?如果是这样,您需要使用 overrideComponent 来成功注入模拟提供程序。

标签: angular unit-testing karma-jasmine rxjs6


【解决方案1】:

我是这样做的:我使用商店发送一个动作来将successful$ 更改为true。现在它是一个集成测试,而不是一个单元测试。

import { ChangePasswordFacade, UserInformationFacade } from '@bis-angular/users';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { StoreModule, Store } from '@ngrx/store';
import { PasswordChangeModalComponent, PasswordChangeFormComponent } from '../../..';
import * as fromFeature from '@bis-angular/users';
import { ReactiveFormsModule } from '@angular/forms';
import { configureTestSuite } from 'ng-bullet';
import { DefaultPopoverComponent } from '@bis-angular/shared-components/pattern-library';

describe('PasswordChangeModalComponent', () => {
  let component: PasswordChangeModalComponent;
  let fixture: ComponentFixture<PasswordChangeModalComponent>;
  let store: Store<fromFeature.ChangePasswordState>;

  const childDefaultPopoverComponent = jasmine.createSpyObj('DefaultPopoverComponent', ['hideModal', 'showModal']);
  const childPasswordChangeFormComponent = jasmine.createSpyObj('PasswordChangeFormComponent', ['setFormControlsToEmpty']);

  configureTestSuite(() => {
    TestBed.configureTestingModule({
      imports: [
        StoreModule.forRoot({}),
        ReactiveFormsModule,
        StoreModule.forFeature(fromFeature.CHANGEPASSWORD_FEATURE_KEY, fromFeature.changePasswordReducer, {
          initialState: fromFeature.changePasswordInitialState
        })
      ],
      declarations: [PasswordChangeModalComponent, DefaultPopoverComponent, PasswordChangeFormComponent],
      providers: [ChangePasswordFacade, UserInformationFacade]
    });
  });

  beforeEach(() => {
    store = TestBed.get(Store);
    spyOn(store, 'dispatch').and.callThrough();

    fixture = TestBed.createComponent(PasswordChangeModalComponent);
    component = fixture.componentInstance;
    component.defaultPopoverComponent = childDefaultPopoverComponent;
    component.watchSuccessful = { unsubscribe: () => {} };

    spyOn(component.watchSuccessful, 'unsubscribe');
    fixture.detectChanges();
  });

  it('should be created', () => {
    expect(component).toBeTruthy();
  });


  describe('showPasswordChangeModal function ', () => {
    it('should call showModal and then hide if successful  ', () => {
      spyOn(component, 'hidePasswordChangeModal');
      spyOn(component.changePasswordService, 'resetState');
      const action = new fromFeature.ChangePasswordSuccessful({});

      store.dispatch(action);
      component.showPasswordChangeModal();

      component.successful$.subscribe((successful: boolean) => {
        expect(component.hidePasswordChangeModal).toHaveBeenCalled();
        expect(component.changePasswordService.resetState).toHaveBeenCalled();
      });
      expect(childDefaultPopoverComponent.showModal).toHaveBeenCalled();
    });
  });
});

【讨论】:

    【解决方案2】:

    因为,您想访问在ChangePasswordFacade 的类级别定义的successful$

    类似:

    export class ChangePasswordFacade{
       successful$:observable // ... something like this
    }
    

    你应该试试useValue:

     configureTestSuite(() => {
        TestBed.configureTestingModule({
          imports: [
            ReactiveFormsModule,
            StoreModule.forRoot({}),
            StoreModule.forFeature(CHANGEPASSWORD_FEATURE_KEY, changePasswordReducer, {
              initialState: changePasswordInitialState
            })
          ],
          declarations: [PasswordChangeFormComponent, DefaultPopoverComponent, PasswordChangeModalComponent],
          providers: [Store, UserInformationFacade, 
                       {provide: ChangePasswordFacade , useValue: 
                         {
                             successful$ : of(true)
                         }
                       }'
                     ]
        });
      });
    

    【讨论】:

    • 感谢您的建议。正如我在最初的问题描述中所指出的,我尝试了您上面建议的确切方法。它没有用。
    • @user372225 : 当你尝试我的建议时,你从succeessful$ 得到了什么价值?
    • 嗨@ShashankVivek,我使用您的建议获得了成功$ 的“假”值。
    • @user372225 即使您正在设置它 of(true) true ?一些变量似乎在操纵它
    猜你喜欢
    • 1970-01-01
    • 2016-10-23
    • 2023-03-15
    • 2019-11-27
    • 1970-01-01
    • 2013-09-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多