如何在单元测试中模拟AngularFire 2服务?

时间:2016-06-26 20:36:09

标签: unit-testing angular jasmine karma-jasmine angularfire2

我尝试使用AngularFire 2 auth为示例Angular 2应用设置单元测试,该组件非常简单:

import { Component } from '@angular/core';
import { AngularFire, AuthProviders } from 'angularfire2';

@Component({
  moduleId: module.id,
  selector: 'app-root',
  templateUrl: 'app.component.html',
  styleUrls: ['app.component.css']
})
export class AppComponent {
  isLoggedIn: boolean;

  constructor(public af: AngularFire) {
    this.af.auth.subscribe(auth => {
      if (auth) {
        this.isLoggedIn = true;
      } else {
        this.isLoggedIn = false;
      }
    });
  }

  loginWithFacebook() {
    this.af.auth.login({
      provider: AuthProviders.Facebook
    });
  }

  logout() {
    this.af.auth.logout();
  }
}

我正在做的就是围绕AngularFire中的loginlogout方法,所以我在考虑使用模拟来检查方法是否被调用,但我不是确定从哪里开始,我尝试在我的spec文件中执行以下操作:

import { provide } from '@angular/core';
import { AngularFire } from 'angularfire2';
import {
  beforeEach, beforeEachProviders,
  describe, xdescribe,
  expect, it, xit,
  async, inject
} from '@angular/core/testing';
import { AppComponent } from './app.component';

spyOn(AngularFire, 'auth');

beforeEachProviders(() => [
  AppComponent,
  AngularFire
]);

describe('App Component', () => {
  it('should create the app',
    inject([AppComponent], (app: AppComponent) => {
      expect(app).toBeTruthy();
    })
  );

  it('should log user in',
    inject([AppComponent], (app: AppComponent) => {
      expect(app.fb.auth.login).toHaveBeenCalled();
    })
  );

  it('should log user out',
    inject([AppComponent], (app: AppComponent) => {
      expect(app.fb.auth.logout).toHaveBeenCalled();
    })
  );
});

但是我不确定如何模仿loginlogout方法,因为它们是auth属性的一部分,有没有办法模拟{ {1}}以及返回的authlogin方法?

3 个答案:

答案 0 :(得分:14)

在此片段中:

beforeEach(() => addProviders([
  AppComponent,
  AngularFire
]);

您设置(或override)将在测试中使用的提供程序。

话虽这么说,你可以创建一个不同的类,如果你愿意,可以使用{ provide: originalClass, useClass: fakeClass }表示法,而不是AngularFire实际的类。

这样的事情:

class AngularFireAuthMock extends AngularFireAuth {           // added this class
  public login() { ... }
  public logout() { ... }
}

class AngularFireMock extends AngularFire {                   // added this class
  public auth: AngularFireAuthMock;
}

beforeEach(() => addProviders([
  AppComponent,
  { provide: AngularFire, useClass: AngularFireMock }         // changed this line
]);

您的测试中的AngularFire将是AngularFireMock s。

答案 1 :(得分:0)

希望这不是主题,而是我发现了如何模拟FirebaseDatabase的最简单解决方案。

var object = function() {
      var obj = { valueChanges() {
            return of({data:'data'});     
        }
      }
      return obj;
    }

providers: [..., { provide : AngularFireDatabase,
        useValue: {object : object }} ]

代替数据:'data'您可以模拟所需的任何数据。可以根据需要修改功能。

答案 2 :(得分:0)

类似于@jan,我使用一些实用程序功能进行了模拟:

import {AngularFireAuth} from '@angular/fire/auth';
import {AngularFireDatabase} from '@angular/fire/database';
import {auth} from 'firebase/app';

import { Observable, of, Subscription } from 'rxjs';

/**
 * Mocks the Firebase auth by automatically logging in.
 */
export const AngularFireAuthMock = jasmine.createSpy('signInWithEmailAndPassword')
      .and.returnValue(Promise.resolve({uid: 'fakeuser'}));

/**
 * Mocks an AngularFireDatabase that always returns the given data for any path.
 */
export function mockAngularFireDatabase(data): AngularFireDatabase {
  return {
    object: (path: string): any => {
      return {
        valueChanges() {
          return of(data);
        }
      }
    }
  } as AngularFireDatabase;
}

,然后您可以像这样在规范中使用它们:

 beforeEach(async () => {
    await TestBed.configureTestingModule({
      declarations: [ TweakComponent ],
      imports: [ MatDialogModule, RouterTestingModule ],
      providers: [
       { provide: MAT_DIALOG_DATA, useValue: {} },
       { provide: AngularFireDatabase, useValue: mockAngularFireDatabase({testdata:'hi'})},
       { provide: AngularFireAuth, useValue: AngularFireAuthMock}
     ],
    })
    .compileComponents();
  });