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

Posted

技术标签:

【中文标题】如何在单元测试中模拟 AngularFire 2 服务?【英文标题】:How to mock AngularFire 2 service in unit test? 【发布时间】:2016-10-28 19:36:47 【问题描述】:

我正在尝试使用 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 方法,所以我正在考虑使用模拟来检查是否调用了这些方法,但我不确定从哪里开始,我试着做我的规范文件中的以下内容:

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 属性的一部分,有没有办法模拟 auth 以及返回的 loginlogout 方法?

【问题讨论】:

感兴趣的读者应该关注this issue,以减少这方面的痛苦。 【参考方案1】:

在这个sn-p中:

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
]);

您测试中的AngularFires 将是AngularFireMocks。

【讨论】:

【参考方案2】:

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

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

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

代替 data:'data' 你可以模拟你需要的任何数据。功能可以随意修改。

【讨论】:

【参考方案3】:

类似于@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();
  );

【讨论】:

以上是关于如何在单元测试中模拟 AngularFire 2 服务?的主要内容,如果未能解决你的问题,请参考以下文章

Angular 2:如何在单元测试时模拟 ChangeDetectorRef

Angular 2 单元测试组件,模拟 ContentChildren

如何在服务层单元测试中模拟数据库结果?

如何在单元测试中模拟缺乏网络连接

如何在 JavaScript 单元测试中模拟 localStorage?

单元测试:如何在反应中模拟 axios?