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

Angular2 (Typescript) + Webpack + Coverage using remap-istanbul result in Error: Could not find source map

I am currently working on different project using Angular 2 with webpack. For these projects we use remap-instanbul to generate coverage reports.

The last week I was upgrading the solution from Angular 2.0 to Angular 2.4 and everything seems to be working as expected except the coverage. During unit testing this error occurred:

Error: Could not find source map for: "C:\Sources\NG2\src\app\app.component.ts"
    at CoverageTransformer.addFileCoverage (C:\Sources\NG2\node_modules\remap-istanbul\lib\CoverageTransformer.js:148:17)
    at C:\Sources\NG2\node_modules\remap-istanbul\lib\CoverageTransformer.js:268:14
    at Array.forEach (native)
    at CoverageTransformer.addCoverage (C:\Sources\NG2\node_modules\remap-istanbul\lib\CoverageTransformer.js:266:24)
....
....
....

Some files however seem to be working and some others not.... Really strange....

But after reading some discussions on the internet I found one solution that was to downgrade the "istanbul-instrumenter-loader" to version 0.2.0. And voilà it worked... So If you like to stop with the first fix you find on the internet.. STOP READING NOW.... Otherwise continue for the even simpler solution.

Ok, so I am not the kind of person to stop with the first fix I find and since you are reading this you are also willing to find the best solution instead of the first solution.. So here is the 'real' solution:

In the webpack configuration for the unit tests check if you have a configuration for typescript like:

ts: {
        compilerOptions: {
            sourceMap: false,
            sourceRoot: './src',
            inlineSourceMap: true
        }
    }

If you have some configuration like this, remove both line about sourcemaps. The configuration will look like this:

ts: {
        compilerOptions: {
            sourceRoot: './src'
        }
    }

Now you can use the newest version of the "istanbul-instrumenter-loader" and it works like a charm.

So have fun with your unit tests and keep up with the coverage ;-).

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.

Webpack: There is another module with an equal name when case is ignored

During working on my project I got the following warning from Webpack:

WARNING: There is another module with an equal name when case is ignored.

After some googling I got a solution! Check all the 'require' keywords used in your project for casing. Well that did not work because all the casings where the same. And then... Suddenly I found the issue. Check both console windows below. The windows above gave the warning. The other window does not give the warning.

Did you see the problem? Well... It is the casing of the drive. The first windows has a lower case c: and the last one has a uppercase C:.

This problem occurred because I opened the first windows from Total Commander and the last window from the Start Menu.

Using the Specification Pattern in Typescript

In some of my C# project I used the Specification pattern for handling business rules. An example of this can be found at https://en.wikipedia.org/wiki/Specification_pattern. But I was wondering if we could create the same experience in Typescript as well. So lets give it a try. I just used the example of the WIKI site but converted it to Typescript.

Specify the interface

Before creating specifications we need the ISpecification interface

export interface ISpecification<T> {
    IsSatisfiedBy(candidate: T): boolean;
}

I don't like the way Wiki creates a ISpecification<T> with the And/Or/Not operators so I created a second interface which contains the operators for composite specifications.

export interface ICompositeSpecification<T> extends ISpecification<T>{
    and(other: ICompositeSpecification<T>): ICompositeSpecification<T>;
    or(other: ICompositeSpecification<T>): ICompositeSpecification<T>;
    not(): ICompositeSpecification<T>;
}

Creating Base classes

First we will create a base class for the composite specification.

export abstract class CompositeSpecification<T> implements ICompositeSpecification<T>
{
    abstract IsSatisfiedBy(candidate: T): boolean;

    and(other: ICompositeSpecification<T>) : ICompositeSpecification<T> {
        return new AndSpecification<T>(this, other);
    }

    or(other: ICompositeSpecification<T>) : ICompositeSpecification<T> {
        return new OrSpecification<T>(this, other);
    }  

    not() : ICompositeSpecification<T>{
        return new NotSpecification<T>(this);
    }
}

As you can see it is also possible in Typescript to use abstract classes with abstract methods! The class must also be 'exported' so it can be used as base class for other Specifications in your project. Now we only need to create the And/Or and Not specification like described in the Wiki page. First the AndSpecification

class AndSpecification<T> extends CompositeSpecification<T>{
    constructor(
        public left:ICompositeSpecification<T>,
        public right:ICompositeSpecification<T>){
        super();
    }

    IsSatisfiedBy(candidate: T) : boolean{
        return this.left.IsSatisfiedBy(candidate) 
           && this.right.IsSatisfiedBy(candidate);
    }
}

As you can see, there is not much code needed for creating this class. The 'left' and 'right' member are specified by the constructor so I can skip the code for settings properties like

this.left = left;
this.right = right

With this AndSpecification in place the other specification are more or less the same except the IsSatisfiedBy method

class OrSpecification<T> extends CompositeSpecification<T>{
    constructor(
        public left:ICompositeSpecification<T>,
        public right:ICompositeSpecification<T>){
        super();
    }

    IsSatisfiedBy(candidate: T) : boolean{
        return this.left.IsSatisfiedBy(candidate) 
           || this.right.IsSatisfiedBy(candidate);
    }
}

class NotSpecification<T> extends CompositeSpecification<T>{
    constructor(
        public spec:ICompositeSpecification<T>){
        super();
    }

    IsSatisfiedBy(candidate: T) : boolean{
        return !this.spec.IsSatisfiedBy(candidate);
    }
}

Example of usage

As an example I created a Person class with name, age and gender property

enum Gender {
  Male,
  Female
}

class Person {
  constructor(
    public name: string,
    public age: number,
    public gender: Gender) {
  }
}

The create a list of Persons and use that inside the app class

import { Component, OnInit } from '@angular/core';
import { CompositeSpecification, ISpecification} from './specifications';

@Component({
  selector: 'my-app',
  templateUrl: 'app/app.component.html'
})
export class AppComponent implements OnInit {
  persons: Person[] = [];

  ngOnInit() {
    this.persons.push(new Person('Mike', 34, Gender.Male));
    this.persons.push(new Person('Perry', 8, Gender.Male));
    this.persons.push(new Person('Gregory', 6, Gender.Male));
    this.persons.push(new Person('Rachel', 3, Gender.Female));
    this.persons.push(new Person('Betty', 35, Gender.Female));
  }
}

I created this example with Angular2 but this will work in any Typescript application

Let say you want to filter the list of persons with the following specification:

  1. All mature persons
  2. All mature female persons
  3. All mature persons and females
  4. All immature persons or female

For this we need the following specification classes

export class IsMatureSpecification extends CompositeSpecification<Person>{
  IsSatisfiedBy(candidate: Person): boolean {
    return candidate.age > 18;
  }
}

export class GenderSpecification extends CompositeSpecification<Person>{
  constructor(private gender: Gender){ super(); }

  IsSatisfiedBy(candidate: Person): boolean {
    return candidate.gender == this.gender;
  }
}

We can add these specifications to the AppComponent class

export class AppComponent implements OnInit {
    persons: Person[] = [];

    private femaleSpec = new GenderSpecification(Gender.Female);
    private matureSpec = new IsMatureSpecification();

    ....
  }

In the AppComponent I created a private method for filtering the list of persons using a specification

  private executeSpecification(spec: ISpecification<Person>) {
    let filteredList: Person[] = [];
    this.persons.forEach(person => {
      if (spec.IsSatisfiedBy(person)) {
        filteredList.push(person);
      }
    });
    return filteredList;
  }

With the private method from above I can easily show you that the specifications work with the following code

  get females() {
    return this.executeSpecification(this.femaleSpec);
  }

  get matureFemales() {
    let matureFemales = this.femaleSpec.and(this.matureSpec);
    return this.executeSpecification(matureFemales);
  }

  get matureOrFemales() {
    let matureFemales = this.femaleSpec.or(this.matureSpec);
    return this.executeSpecification(matureFemales);
  }

  get immatureOrFemales() {
    let matureFemales = this.femaleSpec.or(this.matureSpec.not());
    return this.executeSpecification(matureFemales);
  }

Combine this with the following app.component.html and you can see filtered lists of persons

<h1>Specification Patterns</h1>
<div *ngFor="let person of persons">
    {{person.name}} - {{person.age}}
</div>
<hr>
<div *ngFor="let female of females">
    {{female.name}} - {{female.age}}
</div>
<hr>
<div *ngFor="let female of matureFemales">
    {{female.name}} - {{female.age}}
</div>
<hr>
<div *ngFor="let person of matureOrFemales">
    {{person.name}} - {{person.age}}
</div>
<hr>
<div *ngFor="let person of immatureOrFemales">
    {{person.name}} - {{person.age}}
</div>

This will give the following output in the browser

Conclusion

YES! It is possible and very ease to use the specification pattern in Typescript. 

 

Note: After I finished this blog post I found an npm package that has more or less the same implementation of the specification pattern at: https://www.npmjs.com/package/ts-specification