Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create Mock calls constructor #16

Closed
travelerspb opened this issue Jul 18, 2016 · 11 comments
Closed

Create Mock calls constructor #16

travelerspb opened this issue Jul 18, 2016 · 11 comments

Comments

@travelerspb
Copy link

Im trying to setup mock object like thus
let account: TypeMoq.Mock<IAccount> = TypeMoq.Mock.ofType<IAccount>(Account);

Account class has a constructor, and this constructor is called during Mock object creation.
Im not sure if this is made intentionally or not, but obviously when we write test we want to avoid any logic in mocked classes

@matthieubouvier
Copy link

A workaround is to overload the constructor.

Declare function used for mock creation:

function getMockInstance(classToMock: any) {
  // Save current class prototype
  var oldPrototype = classToMock.prototype;
  // Create empty type and copy the saved prototype
  var newClass = function () {
    return;
  };
  newClass.prototype = oldPrototype;
  // Create a mock of the new class (empty constructor with same functions as classToMock)
  var newInstance = new newClass();
  return TypeMoq.Mock.ofInstance(newInstance);
}

Create mock:
var mockLoggerService: TypeMoq.Mock<LoggerService> = getMockInstance(LoggerService);

@florinn
Copy link
Owner

florinn commented Jul 20, 2016

@travelerspb I can confirm the constructor is called intentionally during mock creation.

Having a fully initialized object makes it possible to call the base class implementation when there's no overriding setup (this behavior may be enabled by setting mock.callBase = true).

Still I believe you've made a valid point.
So while callBase appears to make sense for Mock.ofInstance, for Mock.ofType maybe not so much and the constructor call wouldn't be needed after all.

@matthieubouvierthales Thanks for providing a workaround.

Let me know what you think.

@nertzy
Copy link

nertzy commented Jul 21, 2016

I agree that the original class's constructor should not be called.

We are attempting to mock Angular 2's Router class, and aren't able to because the constructor for Router is complex and has several expectations of how the various fields behave that don't matter for our test. We only care about the external interface of the object, not its internals.

@florinn
Copy link
Owner

florinn commented Jul 22, 2016

@nertzy Have you tried to construct a Router mock by providing mocks as constructor arguments?
Not sure what ctor params has Router, but you could try smth like Mock.ofType(Router, MockBehavior.Loose, arg1, arg2...) where arg1, arg2 are mock objects.

Related to what would happen if the constructor is not called by Mock.ofType, based on the output from TypeScript, it looks like if the constructor is not called then the instance properties are not getting defined and so they're not mockable anymore.
E.g.

class Bar {
        value: string = '';
    }

TS output:

var Bar = (function () {
        function Bar() {
            this.value = '';
        }
        return Bar;
    }());

@florinn
Copy link
Owner

florinn commented Aug 7, 2016

Closing this. IMO losing access to mock instance properties is show stop.

To avoid running constructor logic irrelevant for mock classes, you could provide mocks as constructor arguments.
E.g.

class Type1 {
...
}
class Type2 {
...
}
class Foo {
    constructor(p1: Type1, p2: Type2) {
    ...
    }
}

let mockA1: TypeMoq.Mock<Type1> = TypeMoq.Mock.ofType(Type1);
let mockA2: TypeMoq.Mock<Type2> = TypeMoq.Mock.ofType(Type2);
let mockFoo: TypeMoq.Mock<Foo> = TypeMoq.Mock.ofType2(Foo, [mockA1.object, mockA2.object]);

@florinn florinn closed this as completed Aug 7, 2016
@stijnherreman
Copy link

What if one of the constructor arguments also has a constructor that requires arguments?

It's unfortunate if there are technical limitations preventing a proper solution. In its current state however, I find TypeMoq to be unusable for me.

@florinn
Copy link
Owner

florinn commented Aug 28, 2016

If a constructor argument has a constructor that requires arguments, you can mock the args as necessary.
E.g.

class Type1 {
...
}
class Type2 {
...
}
class Type3 {
...
}
class Type4 {
    constructor(p1: Type1, p2: Type2) {
    ...
    }
}
class Foo {
    constructor(p1: Type3, p2: Type4) {
    ...
    }
}

let mockA1: TypeMoq.Mock<Type1> = TypeMoq.Mock.ofType(Type1);
let mockA2: TypeMoq.Mock<Type2> = TypeMoq.Mock.ofType(Type2);
let mockA3: TypeMoq.Mock<Type3> = TypeMoq.Mock.ofType(Type3);
let mockA4: TypeMoq.Mock<Type4> = TypeMoq.Mock.ofType2(Type4, [mockA1.object, mockA2.object]);
let mockFoo: TypeMoq.Mock<Foo> = TypeMoq.Mock.ofType2(Foo, [mockA3.object, mockA4.object]);

@NagRock
Copy link

NagRock commented Aug 29, 2016

But if another and another mock also has constructor arguments I can get crazy setup for simple test.

@florinn
Copy link
Owner

florinn commented Aug 30, 2016

You can omit any constructor argument if undefined is an acceptable value.

For the example above, creating mockFoo can be as simple as:

let mockFoo: TypeMoq.Mock<Foo> = TypeMoq.Mock.ofType(Foo);

or if just one of the constructor params needs to be mocked:

let mockA3: TypeMoq.Mock<Type3> = TypeMoq.Mock.ofType(Type3);
let mockFoo: TypeMoq.Mock<Foo> = TypeMoq.Mock.ofType2(Foo, [mockA3.object]);

@FLGMwt
Copy link

FLGMwt commented Jun 22, 2017

IMO, no matter how complex, I should be able to mock FooService without any other consideration than Mock.ofType<FooService>() (even of/especially for FooServices's dependencies). For Angular testing, I've been forced to move all of my instance initialization to lazy property initialization because of this:

private cache;
constructor(private cacheService) {
  cache = cacheService.get('myCache');
}`

becomes:

private _cache;
constructor(private cacheService) {}
private get() {
  if (!this._cache) {
    _cache = this.cacheService.get('myCache');
  }
  return this._cache;
}

I'd like to avoid:

  1. Catering my implementation logic to testing limitations
  2. Adding (in my opinion unnecessary) testing overhead to mock potentially unlimited dependency chains in order to test a single system-under-test's dependency

I wouldn't mind opting out of constructor consideration: Mock.ofTypeWithoutConstructor(FooService). Without knowing internals or project opinions, is this feasible?

@florinn
Copy link
Owner

florinn commented Jun 24, 2017

Starting with version 1.0.0, typemoq supports dynamic mocking that does exactly what you want.

Simply create the mock like this and the constructor of your class is not going to get called:

const mock = TypeMoq.Mock.ofType<FooService>();

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants