【问题标题】:How to test a service that interacts with Firebase?如何测试与 Firebase 交互的服务?
【发布时间】:2017-01-06 00:50:25
【问题描述】:

似乎从 Firebase 获取数据的惯例是创建一个在内部使用 AngularFire 的服务类。然后,在您的应用程序的组件中,您可以注入和使用该服务。这使您可以在对组件进行单元测试时轻松地模拟服务。

但是,我仍然不清楚如何测试实际服务本身。

例如,假设我在 Firebase 中有一个 todo list 节点,并且我有一个简单的服务来获取所有 todos,如下所示:

import { Injectable } from '@angular/core';
import { AngularFire } from 'angularfire2';

@Injectable()
export class TodoService {
  constructor(private af: AngularFire) { }

  getAll(): FirebaseListObservable<any[]> {
    return this.af.database.list('todos');
  }
}

您将如何测试该服务是否符合您的预期?

【问题讨论】:

    标签: angular firebase firebase-realtime-database angularfire2


    【解决方案1】:

    Reaz 在他的回答中推荐了 MockFirebase。但是,MockFirebase 不适用于 Firebase 3。有一个名为 firebase-mock 的库,它似乎是由一些与 MockFirebase 相同的人开发的。这增强了 MF,增加了对 Firebase 3 的支持,但是它doesn't appear to play nicely with WebPack yet

    这是我最终做的:

    我突然想到 FirebaseListObservable 继承自 RXJS 的 Observable。因此,您可以模拟对 list 的 AngularFire 调用并让它返回您自己的 Observable。

    一些示例代码显示了这一点:

    todos.service.spec.ts

    let fixtureTodos = [
      { 'text': 'Get milk' },
      { 'text': 'Take out the trash' },
      { 'text': 'Get gas for the car' },
      { 'text': 'Pay parking ticket' },
      { 'text': 'Pick up dry cleaning' },
    ];
    let angularFireDatabaseStub = { list: () => {} };
    let mockTodos$ = Observable.of(fixtureTodos);
    
    describe('TodosService', () => {
      beforeEach(() => {
        spyOn(angularFireDatabaseStub, 'list').and.returnValue(mockTodos$);
    
        TestBed.configureTestingModule({
          providers: [
            TodosService,
            { provide: AngularFireDatabase, useValue: angularFireDatabaseStub },
          ]
        });
      });
    
      it('#getAll', inject([TodosService], (service: TodosService) => {
        let todos$ = service.getAll();
        todos$.subscribe(todos => {
          expect(todos[0].text).toEqual(fixtureTodos[0].text);
          expect(todos[0]).toEqual(jasmine.any(Todo));
        });
      }));
    });
    

    todos.service.ts

    @Injectable()
    export class TodosService {
      constructor(public db: AngularFireDatabase) { }
    
      getAll(): Observable<Todo[]> {
        return this.db.list('todos').map(arr => {
          return arr.map(t => new Todo(t.text));
        });
      }
    }
    

    todo.model.ts

    export class Todo {
      constructor(public text: string) {}
    }
    

    【讨论】:

      【解决方案2】:

      这个问题主要是基于意见的,因此,我只是在这里提出我的意见。

      我会在 Firebase 中创建一个测试节点,该节点与我的开发数据库模型完全相同。所以在编写单元测试时,我会插入一些数据并再次检索它们以匹配我之前插入的数据。

      基本上,Firebase 将数据存储在 Json 结构中。所以我会写一些测试 Json todo 条目。我不会模拟 Firebase 参考对象或连接,因为这基本上是第三方软件。我会在运行单元测试时与 Firebase 建立真正的联系,插入一些数据并将它们检索回来并与我之前插入的数据相匹配。那将是我的方法。

      更新

      是的,您对持续集成有些顾虑。老实说,我以前没有在团队和 CI 服务器上做过这样的事情。我不能保证流畅的体验。

      在寻找更好的解决方案时,我发现 MockFirebase 可能会满足您的目的。你可以看看。

      我想参考this answer,他建议将 Firebase 调用保存在库中。不过,这不考虑 CI 服务器。

      【讨论】:

      • 感谢您的回复。那很有意思。因此,您实际上会在测试中连接到 Firebase。如果我有能力在本地运行 Firebase,我想我会倾向于这样做。但是,我对设置依赖远程服务的自动化测试犹豫不决。我想我的犹豫是它可能会导致 CI​​ 体验的可靠性降低。我很好奇,你过去做过这个并且有过流畅的体验吗?如果是这样,您是在具有持续集成服务器的团队环境中进行的吗?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-12-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多