How to provide/mock Angularfirestore module inside angular component for default test to pass?

匆匆过客 提交于 2020-12-30 07:32:56

问题


How can I provide AngularFirestore module inside my app.component so that my default toBeTruthy() test will pass?

Error: StaticInjectorError(DynamicTestModule)[AppComponent -> AngularFirestore]: 
      StaticInjectorError(Platform: core)[AppComponent -> AngularFirestore]: 
        NullInjectorError: No provider for AngularFirestore!

app.component

export class AppComponent implements OnInit {
  private notesCollection: AngularFirestoreCollection<any>;
  public notes: Observable<any[]>;

  constructor(private afs: AngularFirestore) {}

  ngOnInit() {
    this.notesCollection = this.afs.collection('notes');
    this.notes = this.notesCollection.valueChanges();
  }
}

it's just the default test:

class FirebaseMock implements AngularFirestore {
  app: FirebaseApp;
  firestore: FirebaseFirestore;
  persistenceEnabled$: Observable<boolean>;

  collection<T>(path: string, queryFn?: QueryFn): AngularFirestoreCollection<T> {
    return undefined;
  }

  doc<T>(path: string): AngularFirestoreDocument<T> {
    return undefined;
  }

  createId(): string {
    return undefined;
  }
}

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

  beforeEach(
    async(() => {
      TestBed.configureTestingModule({
        imports: [
          RouterTestingModule,

        ],
        declarations: [ AppComponent ],
        providers: [
          {
            provide: AngularFirestoreModule,
            useClass: FirebaseMock
          },
        ],
      }).compileComponents();
    }),
  );

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

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

回答1:


You have to either mock "AngularFirestore" or inject it as is and create spy on its methods so it won't get called. I would not recommend the second option because it requires the real service to be injected which may depend on other services too. So, you have to inject them as well which may end up requiring millions of services to test just one component. So, let's go with the first option.

If it is commonly used among your component, I'd suggest you create a "stub" module for these kinds of services and import this module in components test modules that you want to test. If it is just for this component, you can create something easy like this: (let's start with easy one and create module later)

app.component.spec.ts

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

    const AngularFirestoreStub = {
        // I just mocked the function you need, if there are more, you can add them here.
        collection: (someString) => {
            // return mocked collection here
        }
    };

    beforeEach(
        async(() => {
           TestBed.configureTestingModule({
               imports: [ RouterTestingModule],
               // I used 'useValue' because it is just a json. If it was class, I'd use 'useClass'
               providers: [{provide: AngularFirestore, useValue: AngularFirestoreStub}]
               declarations: [ AppComponent ]
           }).compileComponents();
        })
    );

    beforeEach(() => {
        fixture = TestBed.createComponent(AppComponent); // Should be fine
        component = fixture.componentInstance;
        fixture.detectChanges();
    });

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

As I said before, if AngularFirestore is a service used by many of your components, then create a stub module somewhere in your project (in my project I created a testing folder and put it next to src)

CommonServiceModuleStub

@NgModule({
    providers: [{provide: AngularFirestore, useClass: AngularFirestoreStub}]
})
export class CommonServiceModuleStub {}

// I'd recommend you put this service in a subfolder.
// This time, I created a class instead of a json. 
// It is because, your other components may require more 'mocked' functions.
// It may be harder to maintain them within a json.
@Injectable()
export class AngularFirestoreStub {
    collection(someString) {
        // return mock collection;
    }
}

Now, instead of providing yourself just import the module we just created

app.component.spec.ts

 ...
 TestBed.configureTestingModule({
     imports: [ RouterTestingModule, CommonServiceModuleStub],
     declarations: [ AppComponent ]
 }).compileComponents();

Option 2

Sometimes, your services are simple ones and you don't want to bother going all the way to "mock" them. Take a look at the following example

app.component.ts

@Component({ ... })
export class AppComponent {
    constructor(private myService: AwesomeService) {}

    doSomethingCool() {
        this.myService.getAwesomeStuff();
    }
}

Let's configure TestBed first,

app.component.spec.ts

 ...
 TestBed.configureTestingModule({
     imports: [ RouterTestingModule],
     // since, 'AwesomeService' is a service on its own and 
     // doesn't require other services, we easily provide it here
     providers: [ AwesomeService ]
     declarations: [ AppComponent ]
 }).compileComponents();

And within a test

it('should do something cool without getting awesome stuff', () => {
    spyOn(component.myService, 'getAwesomeStuff');
    // Note: if you want to spy on it and you want it to get called for real
    // you should do following
    // spyOn(component.myService, 'getAwesomeStuff').and.callThrough();
    // or return fake output
    // spyOn(component.myService, 'getAwesomeStuff')
    //        .and.callFake((arguments, can, be, received) =>  {
    //                          return fake;
    //                      });

    component.doSomethingCool();
    expect(component.myService.getAwesomeStuff).toHaveBeenCalled();
});

For more info, you can take a look at jasmine docs



来源:https://stackoverflow.com/questions/48760093/how-to-provide-mock-angularfirestore-module-inside-angular-component-for-default

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!