测试Angular组件,它根据可观察的服务而变化

时间:2017-07-27 06:34:20

标签: angular unit-testing jasmine observable

我正在尝试测试Angular 4中组件的login()方法,该方法依赖于返回成功或错误的Observable authService

受测试代码:

login() {
    this.loginError = undefined;
    this.loadingService.present('Logging In...');

    this.authService
      .login(this.form.value)
      .subscribe(
        (currentUser: any) => {
          this.loadingService.dismiss();
          this.navCtrl
            .setRoot('TabsPage')
        },
        (error: any) => {
          this.loadingService.dismiss();
          this.loginError = error._body;
          console.error('LoginPage :: Login Error:', error);
        }
      );
}

我知道要成功对此方法进行单元测试,我需要将其隔离。我为authService创建了一个存根,并将其注入TestBed

身份验证存根

export class AuthenticationServiceStub {

  login() {};

  getCurrentUserFromStorage() {};

  logout() {};

};

Testbed config:

TestBed.configureTestingModule({
      declarations: [
        ...components,
      ],
      providers: [
        NavController,
        LoadingService,
        FormBuilder,
        { provide: AuthService, useClass: AuthenticationServiceStub }
      ],
      imports: [
        IonicModule.forRoot(LoginPage),
        PierDataServicesModule.forRoot(),
      ],
    });

据我所知,单元测试login()我必须测试这4件事:

  1. this.loginError设置为undefined
  2. this.loadingService.present('Logging In...')被称为
  3. this.authService.login(this.form.value)被称为
  4. 如果this.authService.login(this.form.value)返回成功,那就是     成功功能块中的两个方法都被调用,如果是     错误,调用错误块中的方法。
  5. 这就是我的规范,(我已经成功测试了1,2,3):

    试验:

    describe('Login()', () => {
    
      let fixture:      ComponentFixture<LoginPage>;
      let instance: any = null;
      let authService: any;
    
      beforeEach(async(() => TestUtils.beforeEachCompiler([LoginPage]).then(compiled => {
    
        fixture = compiled.fixture;
        instance = compiled.instance;
    
        spyOn(instance, 'login').and.callThrough();
        spyOn(instance.loadingService, 'present');
        spyOn(instance.authService, 'login').and.returnValue({ subscribe: () => {} });
    
        instance.login();
    
      })));
    
      it("should be callable", async(() => {
        expect(instance.login).toHaveBeenCalled();
      }));
    
      it("should call loadingService.present ", async(() => {
        expect(instance.loadingService.present).toHaveBeenCalled();
      }));
    
      it("should call authService.login", async() => {
        expect(instance.authService.login).toHaveBeenCalled();
      });
    
    
    });
    

    我无法弄清楚如何测试4号。如何通过成功或错误复制被调用的Observable,并检查这些函数体中的函数是否运行?

    我可以构建模拟login()方法并复制测试代码中的功能,但是测试模拟方法而不是实际代码似乎非常违反直觉。

    我应该使用真正的authService方法并构建一个模拟后端吗?这是有用的吗?如果是这样,怎么样?

    我应该使用Karma spyOn方法并修改它们来做我想要的吗?我想我可以做这样的事情:spyOn(instance.authService, 'login').and.callThrough().and.throwError()但是这种方法没有成功。

1 个答案:

答案 0 :(得分:1)

实际服务不应该参与单元测试,因为这会引入额外的移动部件。

测试promises和observables的一种简洁方法是提供一个真实的,而不是模仿它们的对象:

spyOn(instance.authService, 'login').and.returnValue(Observable.of(null));
...
expect(instance.loadingService.dismiss).toHaveBeenCalled();
expect(instance.navCtrl.setRoot).toHaveBeenCalledWith('TabsPage');
expect(instance.lognError).toBe(...);
...
const _body = {};
spyOn(instance.authService, 'login').and.returnValue(Observable.throw({ _body }));
...
expect(instance.loadingService.dismiss).toHaveBeenCalled();
expect(instance.navCtrl.setRoot).not.toHaveBeenCalled();
expect(instance.lognError).toBe(_body);