2

Similar to this question on typemoq injection, how can I inject ts-mockito objects into angular's TestBed.configureTestingModule()? Any example ts-mockito tests I can find are independent of angular.

Consider the following minmal working example to test- it contains a ProgressComponent, with a ProgressService on the backend that we're mocking. The service is quite trivial:

import { Injectable } from '@angular/core';

@Injectable()
export class ProgressService {
    private currentState: string = '1';

    constructor() {
    }

    setCurrentState(state: string) {
        this.currentState = state;
    }

    getCurrentState(){
        return this.currentState
    }

}

To mock this without ts-mockito, we simply extend the class.

import {ProgressService} from "../../../progress.service";

export class MockProgressService extends ProgressService{}

And then when testing ProgressComponent, the MockProgressService is passed to the TestBed.

import {async, ComponentFixture, getTestBed, TestBed} from '@angular/core/testing'

import { ProgressComponent } from './progress.component'
import {ProgressService} from "../progress.service"
import {MockProgressService} from "../shared/services/progress/progress.mock";

describe('ProgressComponent', () => {
  let injector: TestBed
  let mockService: ProgressService
  let fixture: ComponentFixture<ProgressComponent>
  let component: ProgressComponent

  beforeEach(async() => {
    TestBed.configureTestingModule({
      declarations: [ ProgressComponent ],
      providers: [{provide: ProgressService, useClass: MockProgressService}]
    }).compileComponents()

    injector = getTestBed()
    fixture = TestBed.createComponent(ProgressComponent)
    component = fixture.componentInstance
    mockService = injector.get(ProgressService)

    fixture.detectChanges();
  });
});

The angular test framework introduces dependency injection through the TestBed. How can we use mockito within angular's test setup? For example, how can the snippet injector.get(ProgressService) be made compatible with ts-mockito mocks?

Adam Hughes
  • 14,601
  • 12
  • 83
  • 122
  • 1
    `class MockProgressService extends ProgressService` doesn't make much sense, so it's unclear what is the problem with ts-mockito objects then. You can mock a service with any object you want with `useValue: ...` or `useFactory: () => ...` – Estus Flask Oct 26 '17 at 17:05
  • The angular tutorial creates mock objects by extending the actual classes one is mocking. So MockProgressService is just extending ProgressService (and overwriting not methods in this case). So isntead of useClass, I should useValue are reference to a mockito object that I instantiated manually? – Adam Hughes Oct 26 '17 at 17:08
  • Yes,it's like that. I'm not sure what was the point of that tutorial, but generally extending real class in mocked one is a no-no for practical reasons. – Estus Flask Oct 26 '17 at 17:32

1 Answers1

4

If a provider should be mocked with another object and not class, it should use useValue or useFactory.

For Jasmine it is:

providers: [{
  provide: ProgressService,
  useValue: jasmine.createSpyObj('ProgressService', [...])
}]

And for ts-mockito it can be:

progressServiceMock = mock(ProgressService);
...
providers: [{provide: ProgressService, useValue: instance(progressServiceMock)}]

While inheriting mocked class frim original class like class MockProgressService extends ProgressService usually doesn't have benefits. If the intention is to mock some methods, they can be mocked with jasmine.spy (or a counterpart that current testing framework has) on real class instance or class prototype. And when a class isn't the one that is tested in current unit test, mocking/stubbing all methods and not several is beneficial for test isolation.

Estus Flask
  • 206,104
  • 70
  • 425
  • 565
  • This post on mocks vs spies helped me understand the distinction in the two cases you listed (https://stackoverflow.com/questions/12827580/mocking-vs-spying-in-mocking-frameworks) Thanks for the great answers – Adam Hughes Oct 26 '17 at 17:51
  • You're welcome. Never used the latter ones in real work. Jasmine spies are quite versatile, and for more complex stuff Mocha stubs are good. I'm not comfortable with default behaviour that mockito and typemoq provide. They are primarily intended to stub all class methods automagically, while mocking them greedily with some manual control allows to find mistakes in test code easier. Typemoq has 'strict' mode to address this problem, but it looks like mockito has nothing like that. – Estus Flask Oct 26 '17 at 17:52