ts-mockito icon indicating copy to clipboard operation
ts-mockito copied to clipboard

Adding support for mocking interfaces

Open johanblumenberg opened this issue 7 years ago • 41 comments
trafficstars

This commit adds support for mocking interfaces, like this:

interface Foo {
  bar(value: string): string;
}

let mockedFoo: Foo = imock();
let foo: Foo = instance(mockedFoo);

when(mockedFoo.bar('a')).thenReturn('b');

johanblumenberg avatar Jan 05 '18 10:01 johanblumenberg

Codecov Report

Merging #76 into master will decrease coverage by 1.34%. The diff coverage is 83.33%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master      #76      +/-   ##
==========================================
- Coverage    94.9%   93.56%   -1.35%     
==========================================
  Files          34       34              
  Lines         609      653      +44     
  Branches       70       81      +11     
==========================================
+ Hits          578      611      +33     
- Misses         22       28       +6     
- Partials        9       14       +5
Impacted Files Coverage Δ
src/Spy.ts 80.55% <100%> (ø) :arrow_up:
src/utils/MethodCallToStringConverter.ts 100% <100%> (ø) :arrow_up:
src/MethodStubVerificator.ts 100% <100%> (ø) :arrow_up:
src/MethodToStub.ts 100% <100%> (ø) :arrow_up:
src/ts-mockito.ts 92.75% <75%> (-3.97%) :arrow_down:
src/Mock.ts 91.55% <81.39%> (-4.21%) :arrow_down:

Continue to review full report at Codecov.

Legend - Click here to learn more Δ = absolute <relative> (impact), ø = not affected, ? = missing data Powered by Codecov. Last update c1c7f69...a1baf00. Read the comment docs.

codecov-io avatar Jan 05 '18 11:01 codecov-io

😮 @johanblumenberg this looks great! Interfaces mocking feature has been long awaited. I only got to think about imock function name and add readme. Thanks for contributing!

NagRock avatar Jan 10 '18 14:01 NagRock

@NagRock I added an exception if calling imock() with no Proxy support

johanblumenberg avatar Jan 14 '18 17:01 johanblumenberg

This only supports stubbing methods, am I right? I also tried something like this some time ago but failed to differentiate between properties and method calls.

Markus-Ende avatar Jan 14 '18 18:01 Markus-Ende

@Markus-Ende Yes, only stubbing methods. That's why I made a separate function, imock(), because it works slightly different.

But it's still quite useful to be able to automatically mock interfaces, even though you cannot mock properties on interfaces.

johanblumenberg avatar Jan 15 '18 08:01 johanblumenberg

Hi, what is blocking this PR? Is there anything I can help with?

ilbonte avatar Feb 19 '18 14:02 ilbonte

Looks useful. But I think there should be a type parameter for imock(). so the return types can be defined (just like when using mock()).

let mock = imock<Foo>(); // mock: Foo

let foo = instance(mock); // foo: Foo

EDIT: Sorry, I didn't see there already is a type parameter.

jjordan-dff avatar Feb 23 '18 07:02 jjordan-dff

@NagRock Is there anything blocking this PR?

johanblumenberg avatar Feb 26 '18 14:02 johanblumenberg

any update on this PR? will it be merged soon?

perransw avatar Apr 11 '18 07:04 perransw

+1, when will it merged ?

kwolfy avatar Apr 11 '18 15:04 kwolfy

+2 on merging

@NagRock any ideas when this will be merged and available?

drenner-netspend avatar Apr 16 '18 17:04 drenner-netspend

@johanblumenberg maybe add usage examples to the readme as well?

perransw avatar Apr 19 '18 06:04 perransw

Fixed a problem when retuning mock instances in thenResolve():

The problem is that the Promise.resolve() call will check if the given value is a promise, by checking if it has a function property named then. If so, it believes that the value is a promise, and will wait for it to resolve before returning.

A mock object will intersect all unknown properties and return a stub function, so it will look like a promise to the promise library.

(test "resolves with given mock value" in stubbing.method.spec.ts)

johanblumenberg avatar May 22 '18 09:05 johanblumenberg

Added support for mocking both properties or methods on interfaces.

This will make all properties on the object to be stubbed as properties:

let mock: Foo = imock(MockPropertyPolicy.StubAsProperty);
when(mock.foo).thenReturn('xyz');

This will make all properties on the object to be stubbed as methods:

let mock: Foo = imock(MockPropertyPolicy.StubAsMethod);
when(mock.foo()).thenReturn('xyz');

For classes, the same behavior is kept. It will look at the class to decide which properties are methods and which are properties. The new policy applies to unknown properties and all properties on interfaces.

johanblumenberg avatar May 22 '18 09:05 johanblumenberg

Added support for mocking interfaces containing both properties and methods. The default policy in the previous commit decides what happens with unknown properties. The type of a property can be set by setting up an expectation on the property.

let mock: Foo = imock();

when(mock.method()).thenReturn(1); // Here it is decided that 'method' is a method
when(mock.property).thenReturn(2); // Here it is decided that 'property' is a property

This must be done before the mock instance is used. If not, the default property policy is used to decide if the property is a property or a method.

johanblumenberg avatar May 22 '18 09:05 johanblumenberg

@NagRock would you please consider merging this, this really blocks quite some use cases

cjanietz avatar Sep 13 '18 07:09 cjanietz

Eagerly waiting for this to be merge

DonJayamanne avatar Oct 04 '18 02:10 DonJayamanne

Is there an alternative way to mock an interface like this at this point? (without casting to any)

jensbodal avatar Oct 30 '18 19:10 jensbodal

ping

johanblumenberg avatar Nov 29 '18 10:11 johanblumenberg

Is there any updates on this, this is a blocker for us to use ts-mockito, specially because we have to implement interface to mock them. Can you please merge this soon, I've waiting for the this feature for so long, thank you for this awesome ts mocking lib.

kouros51 avatar Dec 27 '18 15:12 kouros51

@kouros51, @DonJayamanne, @cjanietz, @drenner-netspend, @kwolfy, @perransw, @Markus-Ende

I published a forked package, that contains the possibility to mock interfaces: https://www.npmjs.com/package/@johanblumenberg/ts-mockito

It has support for mocking methods and properties on interfaces.

johanblumenberg avatar Dec 28 '18 15:12 johanblumenberg

Pushed another commit with updated README, describing imock()

johanblumenberg avatar Dec 28 '18 15:12 johanblumenberg

@johanblumenberg thanks for that. This was very helpful.

kouros51 avatar Dec 31 '18 22:12 kouros51

@johanblumenberg

I tried your build, and I was having issues with mocking properties defined in interfaces. For example:

interface Foo {
  someProperty: string;
}

describe('Tests', () => {
  it('Should work', () => {
    const fooMock: Foo = imock();
    when(fooMock.someProperty).thenReturn('hello');
    const foo = instance(foo);
    expect(foo.name).to.equal('hello');
  });
});

I rewrote this test from memory, I don't have the machine that ran this test, but you get the general gist of it. The explicit error was something like "could not reference 'add' property of undefined". I can't remember if the error occurs from the when clause or the expect clause.

Has anyone else seen this?

aserra54 avatar Jan 06 '19 15:01 aserra54

Hm... never mind. I tried to reproduce it locally with a much smaller sample, and it worked fine. The below code passes just fine:

import { imock, instance, when } from '@johanblumenberg/ts-mockito';
import { expect } from 'chai';

interface Foo {
  someProperty: string;
}

describe('Test', () => {
  it('Should work', () => {
    const fooMock: Foo = imock();
    when(fooMock.someProperty).thenReturn('hello!');
    const foo = instance(fooMock);
    expect(foo.someProperty).to.equal('hello!');
  });
});

aserra54 avatar Jan 06 '19 16:01 aserra54

@NagRock Is this repository still maintained? This review was opened over a year ago, and despite being incredibly useful, hasn't been merged into master.

Essentially, I work on a project that heavily requires mocks, and we opted to go with ts-mockito over typemoq. Is it worth it to continue to wait for ts-mockito to merge this feature (among others in), or should we start looking at migrating to a new framework?

aserra54 avatar Mar 18 '19 18:03 aserra54

@aserra54 I published my fork as a package on npm: https://www.npmjs.com/package/@johanblumenberg/ts-mockito

Feel free to use it, or to suggest other PR's that I should add to it.

johanblumenberg avatar Mar 18 '19 18:03 johanblumenberg

Unfortunately this PR doesn't support getters / setters mocking. Thats the reason why this is not merged.

So this will break some functionalities or we will have to remove getters / setters feature. This will also break existing projects that uses this functionality.

NagRock avatar Mar 18 '19 20:03 NagRock

@NagRock Is this repository still maintained? This review was opened over a year ago, and despite being incredibly useful, hasn't been merged into master.

There is no good solution for interface mocking (or maybe there is but I dont have it). One will break current API, and this one will break getters / setters functionality.

NagRock avatar Mar 18 '19 20:03 NagRock

Thanks for closing this PR, at least we have a resolution.

DonJayamanne avatar Mar 18 '19 23:03 DonJayamanne