0

I have a angularfire2 function that I'd like to test. The issue I'm running into is I have no idea how to mock the anonymous function call made by this.db.collection() let along how to mock the piped functions. I'd like to test what value was passed to the ref.orderBy function, and the results returned by the pipe. How would I got about doing this?

   sortedList(collection, orderByProperty){
      return this.db.collection(collection, ref => {
          return ref.orderBy(orderByProperty, 'asc')
        })
        .snapshotChanges().pipe(
            map(actions => actions.map(a => {
              const data = a.payload.doc.data();
              const id = a.payload.doc.id;
              return { id, ...data };
            }))
          );
    }  

..

import { TestBed } from '@angular/core/testing';

import { of } from "rxjs";
import { FirebaseService } from './firebase.service';
import { AngularFirestore } from '@angular/fire/firestore';

describe('FirebaseService', () => {

  let service: FirebaseService;
  const mockedData = [{
    payload:{
      doc:{
        id:"zyx",
        data:()=>{
          return {hello:"world"}
        }
      }
    },

  },{
    payload:{
      doc:{
        id:"abc",
        data:()=>{
          return {hello:"goodbye"}
        }
      }
    }
  }]


var afSpy = jasmine.createSpyObj('AngularFirestore', ['collection', 'snapshotChanges', 'pipe']);
afSpy.collection.and.returnValue(afSpy);
afSpy.snapshotChanges.and.returnValue(afSpy); 
afSpy.pipe.and.returnValue(of(mockedData))


  beforeEach(() => {
    TestBed.configureTestingModule({
      providers:[
        { provide: AngularFirestore, useValue: afSpy }
      ],      
    })
    service = TestBed.get(FirebaseService); //get the testbed and set it so we can use it in our functions
  });
it('should return a sorted list', () => {
  service.sortedList('fakeCollection', 'id').subscribe(res=>{
    expect(res).toEqual([{hello:"world", id:"zyx"}, {hello:"goodbye", id:"abc"}])
  })
  expect(afSpy.pipe).toHaveBeenCalled();

});


});

......................................................................................

Justin Young
  • 2,393
  • 3
  • 36
  • 62
  • You are not giving any hints really on what types you are using(e.g. actions) but if it's about testing different states in the observable chain you can use [Marble testing](https://github.com/cartant/rxjs-marbles) – Lucho Dec 11 '18 at 21:45
  • updated my answer. – Justin Young Dec 11 '18 at 22:03
  • Here's a good example on how [marble test](https://stackoverflow.com/questions/42732988/how-do-i-test-a-function-that-returns-an-observable-using-timed-intervals-in-rxj/42734681#42734681) look like in the familar scenario as you want to test it (in rxjs5 so you have to adjust to rxjs6) – Lucho Dec 11 '18 at 22:12
  • Did the answer below help you? – dmcgrandle Jan 02 '19 at 21:48

1 Answers1

0

Justin, this looks familiar! :)

I set this problem up in a Stackblitz similar to the last problem. This time I made an effort to change as little of your code as possible, keeping what I hope was your basic structure. Below is the describe() that I came up with. All the details in the Stackblitz.

I also had to add FirebaseService to the providers array of the TestBed to get the spec to run without error.

It looks like snapshotChanges() returns the Observable this time, so I nested the insideCollection spyObject as the return value of the collection() method, then set the return value of snapshotChanges() to an Observable of your mockData array using of().

As you can see in the Stackblitz, your it() spec runs just fine just as you originally wrote it without modification.

describe('FirebaseService', () => {

  let service: FirebaseService;
  const mockedData = [{
    payload:{
      doc:{
        id:"zyx",
        data:()=>{
          return {hello:"world"}
        }
      }
    },

  },{
    payload:{
      doc:{
        id:"abc",
        data:()=>{
          return {hello:"goodbye"}
        }
      }
    }
  }]
  var insideCollection = jasmine.createSpyObj('collection', ['snapshotChanges']);
  var afSpy = jasmine.createSpyObj('AngularFirestore', ['collection']);
  afSpy.collection.and.returnValue(insideCollection);
  insideCollection.snapshotChanges.and.returnValue(of(mockedData));
  // afSpy.pipe.and.returnValue(of(mockedData)) <-- .pipe is already a method on an Observable, no need for this.


    beforeEach(() => {
      TestBed.configureTestingModule({
        providers:[
          FirebaseService,
          { provide: AngularFirestore, useValue: afSpy }
        ]
      })
      service = TestBed.get(FirebaseService); //get the testbed and set it so we can use it in our functions
    });

  it('should return a sorted list', () => {
    service.sortedList('fakeCollection', 'id').subscribe(res=>{
      expect(res).toEqual([{hello:"world", id:"zyx"}, {hello:"goodbye", id:"abc"}])
    })
    expect(insideCollection.snapshotChanges).toHaveBeenCalled();
  });


});

I hope this helps. :)

dmcgrandle
  • 5,934
  • 1
  • 19
  • 38