vendredi 1 juillet 2016

Angular 2 RC Unit Testing how to mock injections

I am trying to write some unit tests for an application I have been building but I'm running into issues with mocking injections on the various components/services. I've been reading articles and trying various things for a few hours now but so far been unable to find any examples of the correct way to configure the mocks, however I appear to be close...

I have the following code for my test for a DashboardComponent:

import {
  beforeEach,
  beforeEachProviders,
  describe,
  expect,
  inject,
  injectAsync,
  it
} from '@angular/core/testing';
import { provide } from '@angular/core';
import { Observable } from 'rxjs/Observable';

import { DashboardComponent } from './dashboard.component';
import { ProjectService } from '../../services/project/project.service';
import { Model3DService } from '../../services/model3D/model3D.service';

class MockProjectService {
  public find (model: any): void {}
}

class MockModel3DService {
  public get3DModels (): Observable<any> {
    return;
  }
}

fdescribe('Dashboard Component', () => {
  beforeEachProviders(() => {
    return [
      DashboardComponent,
      provide(ProjectService, {
        useClass: MockProjectService
      }),
      provide(Model3DService, {
        useClass: MockModel3DService
      })
    ];
  });

  describe('foo', () => {
    it('should do stuff', (inject([DashboardComponent], (dashboardComponent: DashboardComponent) => {
      dashboardComponent.easyTest();
      expect(dashboardComponent.foo).toEqual('foobar');
    })));
  });

  describe('ngOnInit', () => {
    it('should call model3DService.get3DModels on init', (inject([DashboardComponent, Model3DService], (dashboardComponent: DashboardComponent, model3DService: MockModel3DService) => {
      dashboardComponent.ngOnInit();
      expect(model3DService.get3DModels()).toHaveBeenCalled();
    })));
  });
});

A couple things to note:

In the examples I've found they extend the real class when they create a mock class. i.e. class MockProjectService { would be class MockProjectService extends ProjectService { however that appears to inject the actual class into the component because I then get errors saying I need to inject every service that is injected into ProjectService, and then inject every injection that each of those injections have, so on and so forth. Obviously that isn't what I want as the tests for DashboardComponent shouldn't care about the actual code behind anything injected into DashboardComponent or any of its dependencies.

By removing the extends bit of code I was able to get my code to run a simple test where I call a method on DashboardComponent and expect a string to have been updated, it works! I thought I had this nailed, but then I tried to write a test that calls a method on a mocked service, but for some reason the mocked service is empty! :( In the second test above you'll notice I expect model3DService.get3DModels to have been called, however I get an error that get3DModels is undefined. I console logged the value of model3DService in my component on the line above where it tries to use model3DService.get3DModels and the log shows me that model3DService is set to MockModel3DService{} which is missing the method I defined inside MockModel3DService in my test...

How are you all setting up your mocks so you don't have to go down an endless rabbit hole of injections?

Aucun commentaire:

Enregistrer un commentaire