【问题标题】:Mocking Firestore collection in angular TestBed在角度 TestBed 中模拟 Firestore 集合
【发布时间】:2018-04-10 05:12:18
【问题描述】:

我想为使用 Firestore 的组件编写一个单元测试,但遇到了 Firebase 集合模拟问题。

SUT

export class TobjectsListComponent implements OnInit {
...
    constructor(private db: AngularFirestore) {
      this.tobjectDatabase = new TobjectDatabase(db);
    }
...
}

export class TobjectDatabase {
  /** Stream that emits whenever the data has been modified. */
  dataChange: BehaviorSubject<TObject[]> = new BehaviorSubject<TObject[]>([]);
  get data(): TObject[] { return this.dataChange.value; }

  constructor(private db: AngularFirestore) {
    this.db.collection<TObject>('tobjects').valueChanges()
      .subscribe(data => { this.dataChange.next(data); });
  }
}

测试

class AngularFirestoreMock extends AngularFirestore {
  public collection<TObject>(name: string, queryFn?: QueryFn): AngularFirestoreCollection<TObject> {
    const ref = this.firestore.collection('tobjects');
    if (!queryFn) { queryFn = (ref) => ref; }
    return new AngularFirestoreCollection<TObject>(ref, queryFn(ref));
  }
}

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

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [MaterialModule],
      declarations: [TobjectsListComponent],
      providers: [{ "provide": AngularFirestore, "useValue": AngularFirestoreMock }],
      schemas: [CUSTOM_ELEMENTS_SCHEMA]
    })
  .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(TobjectsListComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

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

试运行

运行测试后,我收到错误 TypeError: this.db.collection is not a function

从堆栈跟踪信息中,我可以读到该错误源于此行和字符 this.db.**c**ollection&lt;TObject&gt;('tobjects').valueChanges()
TobjectDatabase 类中。

TypeError: this.db.collection is not a function
at new TobjectDatabase (http://localhost:9876/_karma_webpack_/webpack:/C:/git/volago2/src/app/tobjects-admin/tobjects-list/tobjects-list.component.ts:82:13)
at new TobjectsListComponent (http://localhost:9876/_karma_webpack_/webpack:/C:/git/volago2/src/app/tobjects-admin/tobjects-list/tobjects-list.component.ts:24:28)
at createClass (http://localhost:9876/_karma_webpack_/webpack:/C:/git/volago2/node_modules/@angular/core/@angular/core.es5.js:10933:1)
at createDirectiveInstance (http://localhost:9876/_karma_webpack_/webpack:/C:/git/volago2/node_modules/@angular/core/@angular/core.es5.js:10764:22)
at createViewNodes (http://localhost:9876/_karma_webpack_/webpack:/C:/git/volago2/node_modules/@angular/core/@angular/core.es5.js:12212:34)
at createRootView (http://localhost:9876/_karma_webpack_/webpack:/C:/git/volago2/node_modules/@angular/core/@angular/core.es5.js:12107:1)
at callWithDebugContext (http://localhost:9876/_karma_webpack_/webpack:/C:/git/volago2/node_modules/@angular/core/@angular/core.es5.js:13493:26)
at Object.debugCreateRootView [as createRootView] (http://localhost:9876/_karma_webpack_/webpack:/C:/git/volago2/node_modules/@angular/core/@angular/core.es5.js:12810:1)
at ComponentFactory_.webpackJsonp.../../../core/@angular/core.es5.js.ComponentFactory_.create (http://localhost:9876/_karma_webpack_/webpack:/C:/git/volago2/node_modules/@angular/core/@angular/core.es5.js:9872:26)
at initComponent (http://localhost:9876/_karma_webpack_/webpack:/C:/git/volago2/node_modules/@angular/core/@angular/core/testing.es5.js:889:1)

怎么了?我怎么能嘲笑这个集合?

【问题讨论】:

    标签: angular unit-testing firebase google-cloud-firestore


    【解决方案1】:

    好的,我终于成功地模拟了 Firestore 集合。我举一个不同服务的例子:

    SUT

    import { Injectable } from '@angular/core';
    import { AngularFirestore } from 'angularfire2/firestore';
    import { Administrative } from '../model/administrative';
    
    @Injectable()
    export class AdministrativeService {
    
      administratives: Administrative[] = [];
    
      constructor(private db: AngularFirestore) {
       this.db.collection<Administrative>('administrative').valueChanges()
        .subscribe(data => this.administratives = data);
      }
    
      getPath(uname: string): string[] {
        let current = this.administratives.find((a: Administrative) => a.uname === uname);
        const result: string[] = [current.name];
    
        while (current.parent) {
          current = this.administratives.find((a: Administrative) => a.uname === current.parent);
         result.unshift(current.name);
        }
    
        return result;
      }
    }
    

    测试

    import { TestBed, inject } from '@angular/core/testing';
    import { AdministrativeService } from './administrative.service';
    import { AngularFirestore } from 'angularfire2/firestore';
    import { Observable } from 'rxjs/Rx';
    import { Administrative } from '../model/administrative';
    
    const input: Administrative[][] = [[
      { name: 'Polska', uname: 'polska', parent: ''},
      { name: 'Dolnośląskie', uname: 'dolnoslaskie', parent: 'polska'},
      { name: 'Wrocław', uname: 'wroclaw', parent: 'dolnoslaskie'}
    ]];
    
    const data = Observable.from(input);
    
    const collectionStub = {
      valueChanges: jasmine.createSpy('valueChanges').and.returnValue(data)
    }
    
    const angularFiresotreStub = {
      collection: jasmine.createSpy('collection').and.returnValue(collectionStub)
    }
    
    describe('AdministrativeService', () => {
      let service: AdministrativeService;
      let angularFirestore: AngularFirestore;
    
      beforeEach(() => {
        TestBed.configureTestingModule({
          providers: [
            AdministrativeService,
            { provide: AngularFirestore, useValue: angularFiresotreStub }
          ]
        });
    
        service = TestBed.get(AdministrativeService);
        angularFirestore = TestBed.get(AngularFirestore);
      });
    
      it('should be created', () => {
        expect(service).toBeTruthy();
        expect(angularFiresotreStub.collection).toHaveBeenCalledWith('administrative');
      });
    
      it('gets hierarchy path', () => {
        const result = service.getPath('wroclaw');
        expect(result).toEqual(['Polska', 'Dolnośląskie', 'Wrocław']);
      });
    
    });
    

    【讨论】:

      【解决方案2】:

      在我的案例中有效的模式

      服务:

      import { AngularFirestore } from '@angular/fire/firestore';
      
      public db: AngularFirestore                                    // in constructor
      
      updateArticle(someId, data) {
        this.db.collection('articles').doc(someId).update({ some: data });
      }
      

      测试:

      const updateSpy: Spy = jasmine.createSpy('update');
      spyOn(service.db, 'collection').and.returnValue({
        doc: (data: any): any => {
          return {
            update: updateSpy,
          };
        },
      });
      service.updateArticles('123', 'lol');
      expect(service.db.collection).toHaveBeenCalledWith('articles');
      expect(updateSpy).toHaveBeenCalledWith({ some: 'lol' });
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-12-19
        • 1970-01-01
        • 2016-03-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多