Typescript Mocking Library (ts-mocks) now supports Generic methods better

The last few years I've created a library for mocking object in Typescript. A long time ago I wrote an article about it:

http://www.ilove-it.com/post/2016/07/28/mock-your-typescript-classes-and-interfaces-the-easy-way

A long time there was really one thing that I wanted to fix and that is mocking generic methods:

https://github.com/jjherscheid/ts-mocks/issues/9.

So what is the problem?

Typescript can determine the the return value for the mocks methods/properties. But with generic methods this will not work because there is no option to specify the type that will actually be used in the code. I will try to explain this using an example.

Let say you have a service with the following interface:

export interface SomeService {
    get<T>(index: number): Observable<T>;
}

In your application you use the service to returns users:

someService.get<User>(10).subscribe((user) => /* do something with user */ );

If you would like to mock this in you unit test normally you would write:

mockSomeService
    .setup(ss => ss.get)
    .is((value) => of(someUser));
 
// or
 
mockSomeService
    .extend({ get: (value) => of(someUser)});
Unfortunately typescript will complain about the fact that of(someUser) is not of type Observable<T>:
Type 'Observable<User>' is not assignable to type 'Observable<T>'.
Type 'User' is not assignable to type 'T'.

Now the as<T>() comes into the rescue. With this method you can overrule the return value that is automatically determined by the setup() method.
Note: Please note that this can conflict with the real code if not used appropriately, so use at your own risc

mockSomeService
    .setup(ss => ss.get)
    .as<(number) => Observable<User>>()
    .is((value) => of(someUser));

With this typescript will not complain anymore. Great! ;-)

So don't wait any longer and update your solution to ts-mocks 2.6.0

Mock your Typescript Classes and Interfaces (the easy way) [Angular 2.0.0 Final]

Updated to Angular 2.0.0 Final Release (using TestBed instead of addProviders) and with Spy functionality

Code: https://github.com/jjherscheid/ts-mocks
Npm: https://www.npmjs.com/package/ts-mocks

If you are familiar with Unit Testing in Typescript / Angular2, you may have created a lot of Mock objects for your unit tests like:

// Mocks the CookieService from angular2-cookie
class MockCookieService {
  public get(key: string): string { return null; }
  public put(key: string, value: string) { }
}

Such a Mock object does not have to implement all the methods and can be used by Injecting or Providing the class like so:

let cookiesService: CookieService;

// Add the providers
beforeEach(() => {
  TestBed.configureTestingModule({
    ...,
    providers: [{ provide: CookieService, useClass: MockCookieService }]
    });
});

// Inject values
beforeEach(inject([ ..., CookieService], (... , _cookieService: CookieService) => {
  ...
  cookieService = cookieSrv;
}));

What I don't like about this approach is the way the Mock is created. It should have the same methods, but there is nothing that guarantees that the interface is the same as the original object you want to Mock.

To overcome this problem I wanted to create some Mock<T> object that can be used to create your Mock object with the intellisense you want.

The idea is to be able to specify the object like so:

// Mock for the CookieService from angular2-cookie
mockCookieService = new Mock<CookieService>();
mockCookieService.setup(ls => ls.get);
mockCookieService.setup(ls => ls.put);

Where is it possible to setup values for properties and methods. The Mock class can then be used in the beforeEach the following way:

// Create a variable for the Mock<T> class
let mockCookiesService: Mock<CookieService>;

// NOTE: Change the useClass to useValue and use the 

beforeEach(() => {
  // Create new version every test
  mockCookieService = new Mock<CookieService>();

  // Setup defaults
  mockCookieService.setup(ls => ls.get);
  mockCookieService.setup(ls => ls.put); 

  TestBed.configureTestingModule({
    ...
    providers: [{ provide: CookieService, useValue: mockCookieService.Object }]
  });
});

In you unit tests it is now possible to use the mockCookiesService:

it('using with default setup from beforeEach', () => {
  let r = sut.getValue('Test');
  expect(r).toEqual(null);
});

it('setup different value in test', () => {
  mockCookieService.setup(ls => ls.get).is(key => key + '-mock');

  let r = sut.getValue('Test');
  expect(r).toEqual('Test-mock');
  // integrated spy
  expect(cookieService.get).toHaveBeenCalled();
});

I created a small library that makes it possible to use Mock objects like about. The code can be found at: https://github.com/jjherscheid/ts-mocks and is also available as npm package at: https://www.npmjs.com/package/ts-mocks.

Or use the npm install:

npm install ts-mocks

 

I you have any ideas feel free to fork the git repo and make suggestions! I hope you enjoy the solution.