jsmocha icon indicating copy to clipboard operation
jsmocha copied to clipboard

Support for mocking new instances

Open has207 opened this issue 14 years ago • 7 comments

I didn't see this mentioned in the docs so I've worked around it for now, but it would nice for jsmocha to support it natively. The workaround:

var saved = foo.bar.Constructor; var Temp = function() {}; foo.bar.Constructor.prototype = new Temp(); foo.bar.Constructor.prototype.expects('someMethod'); // call the code foo.bar.Constructor = saved;

The painful part is the save/restore, though it shouldn't really be all that necessary to create the Temp constructor either.

Maybe something like:

mock_object = {}; foo.bar.Constructor.new_instances(mock_object); mock_object.expects('someMethod'); // call the code

has207 avatar Apr 08 '11 08:04 has207

Hi,

I assume you're trying to mock a function for all instances of a class? Something like this...

var CoolThing = function(){};

CoolThing.prototype = {
  say_hello: function(){
    alert('hello world');
  }
};

var really_cool_thing = new CoolThing();
var another_cool_thing = new CoolThing();

In this case mocking the prototype is the correct way to mock any instance of CoolThing's say_hello function, for example:

CoolThing.prototype.expects('say_hello');

Then calling either really_cool_thing.say_hello() or another_cool_thing.say_hello() would satisfy the expectation.

kernow avatar Apr 08 '11 15:04 kernow

Not exactly, I want to mock a constructor to ensure that a fake object is returned (and code in the real constructor doesn't get executed).

has207 avatar Apr 09 '11 16:04 has207

Could you provide a code example to illustrate this. It's not clear what you're trying to do in the first example you gave.

On 9 Apr 2011, at 17:31, has207 [email protected] wrote:

Not exactly, I want to mock a constructor to ensure that a fake object is returned (and code in the real constructor doesn't get executed).

Reply to this email directly or view it on GitHub: https://github.com/kernow/jsmocha/issues/1#comment_977604

kernow avatar Apr 09 '11 17:04 kernow

Let's say in my code I have a constructor that generates CoolThing objects:

var CoolThing = function() {
  // call some expensive initialization code
};
var foo = new CoolThing();

After that all sorts of things are done to foo. So in my test I want to ensure that this constructor instead returns fake objects I can define in the test, and ensure foo is set to that object without calling the expensive init code.

Ignore the expects() bits in my previous example, that was just a distraction from the main issue. What I'd basically like to see is the ability to hook into the call to "new" and make it generate a fake object. It's not hard to do, as in my initial example, but annoying to save/restore the constructor function, and I think jsmocha can help here with some special syntax.

has207 avatar Apr 10 '11 01:04 has207

Ok, I understand you're wanting, to mock object constructors when new is called. This is something I've wanted to do in the past too. The problem is that new is a special JavaScript command and I don't know of a way to mock this in a way that would work properly, or even it it would be a desirable thing to do.

I've had a think about how to implement a way of mocking constructors, and something like this may be possible

var CoolThing = function() {
  // call some expensive initialization code
};

// special call to mock out the constructor
new ConstructorMock(CoolThing);

// this would call the mock constructor rather than the actual constructor
var foo = new CoolThing();

ConstructorMock would do something like clone all of the class and prototype functions and properties of CoolThing onto a new mock object and replace CoolThing with this cloned object.

kernow avatar Apr 11 '11 15:04 kernow

That sounds about right :)

You just also need a way to make the fake constructor return a custom object:

new ConstructorMock(CoolThing, object_to_instantiate);

Or something along those lines. Though I actually think adding a method to do this rather than baking it into the constructor for a ConstructorMock class would make for a better API, something along the lines of:

CoolThing.new_instances(object_to_instantiate);

That way you can also chain it just like the others, and attach expectations, arg matching etc:

CoolThing.passing(arg1, arg2).at_least(1).new_instances(object_to_instantiate);

I have something similar working in Python Flexmock if you want to take a look:

http://has207.github.com/flexmock/user-guide.html#override-new-method-on-a-class-and-return-fake-instances

has207 avatar Apr 11 '11 15:04 has207

I was thinking along the same lines. Maybe something like this...

// mocking
CoolThing.expects_new().passing(...).twice().returns('something');

// stubbing
CoolThing.stubs_new().passing(...).returns('something');

// spies
CoolThing.spies_new().passing(...).returns('something');

Or possibly CoolThing.expects_new_instance(), CoolThing.stubbs_new_instance() and CoolThing.spies_new_instance(). I believe there would still need to be a way to specify you want to mock a constructor which could be done with either:

new ConstructorMock(CoolThing);
// or
new Mock(CoolThing, { constructor: true });

kernow avatar Apr 11 '11 15:04 kernow