Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mocking Firestore collection in angular TestBed

I'd like to write a unit test for my component which uses Firestore and I encountered an issue with Firebase collection mocking.

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

TEST

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();
  });
});

Test run

Once I run the test I get an error TypeError: this.db.collection is not a function

From stack trace info I can read that error have an origin in this line and character this.db.**c**ollection<TObject>('tobjects').valueChanges()
in TobjectDatabase class.

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)

What's wrong? How could I mock this collection?

like image 318
Walter Luszczyk Avatar asked Oct 28 '17 21:10

Walter Luszczyk


1 Answers

Ok, I finally have managed mocking of Firestore collections. I give an example of different service:

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;
  }
}

TEST

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

});
like image 55
Walter Luszczyk Avatar answered Oct 13 '22 14:10

Walter Luszczyk