-
Notifications
You must be signed in to change notification settings - Fork 142
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
Mocking a Class #157
Comments
As a temporary workaround, we've gotten around this issue by doing the following: function mockClass(clazz) {
let mock = function() {};
let prototype = _(Object.getOwnPropertyNames(clazz.prototype))
.map((property) => [property, td.function(property)])
.fromPairs()
.value();
mock.prototype = prototype;
return mock;
}
function replaceClass(path) {
let clazz = require(path);
let mock = mockClass(clazz);
td.replace(path, mock);
return mock;
} However, these solutions are quick hacks to enable mocking classes. They likely won't work in all circumstances. It would be great to have this behavior built into testdouble.js. |
There has been various talk of this in other issues and I agree it's time we figured this out. I don't see (as yet) any reason it shouldn't be possible. It's going to be a little bit difficult to test this except through our babel example project, so I'm starting there with some fresh examples of class replacement. See this as a starting point: https://github.com/testdouble/testdouble.js/pull/158/files#diff-6cfe233169444f306186c38b5c879c74R2 Next step I'm going to go peek at other discussion of this and then figure out an approach to implementing it. |
Wow, that was really fast! I'll give the new changes a shot tomorrow. Thanks @searls! |
I don't quite understand this, so enlighten me. This and #158 sound like testdouble supports ES6 native classes now, but it doesn't and you need babal-plugin-transform-es2015-classes, right? Am I supposed to use the transformer (via the es2015 preset or the env one or whatever) to use native classes as examples/babel suggests?
class A {}
var FakeA = require("testdouble").constructor(A);
new FakeA(); $ npm i testdouble
$ node test.js
/tmp/td/node_modules/testdouble/lib/constructor.js:41
var _this = _possibleConstructorReturn(this, (TestDoubleConstructor.__proto__ || Object.getPrototypeOf(TestDoubleConstructor)).apply(this, arguments));
^
TypeError: Class constructor A cannot be invoked without 'new'
at new TestDoubleConstructor (/tmp/td/node_modules/testdouble/lib/constructor.js:41:134)
at Object.<anonymous> (/tmp/td/test.js:5:1)
at Module._compile (module.js:571:32)
at Object.Module._extensions..js (module.js:580:10)
at Module.load (module.js:488:32)
at tryModuleLoad (module.js:447:12)
at Function.Module._load (module.js:439:3)
at Module.runMain (module.js:605:10)
at run (bootstrap_node.js:423:7)
at startup (bootstrap_node.js:147:9) With transform-es2015-classes, I got no error. $ npm i babel-cli babel-plugin-transform-es2015-classes
$ $(npm bin)/babel-node --plugins transform-es2015-classes test.js
# no error |
I'm only glancing at this from my phone but that's probably because without transpilation your runtime supports classes and enforces their runtime with the rule that they must be instaniated with the new keyword. But when you transpile them, babel is converting them to normal constructor functions that's don't care whether you invoke them with new at runtime |
(In case it's not clear I don't think your issue has anything to do with testdouble.js itself) |
I am having the same issue as @kealthou. Node version 6.x, no babel used i the test scenario |
Can you please provide a minimum example project that reproduces this, @svyatogor? I don't understand the issue. |
@searls sure, here is minimal sample: https://github.com/svyatogor/td-node6-157 this is not exactly the setup I have in my project, as I use ES6-style import/export default, but the resulting error is exactly the same |
Ok, I figured out the root cause. testdouble.js is creating a custom class which var _this = _possibleConstructorReturn(this, (TestDoubleConstructor.__proto__ || Object.getPrototypeOf(TestDoubleConstructor)).apply(this, arguments)); Which invokes the original constructor without It looks like @jdalton had a PR to babel to fix this but it was never merged. babel/babel#3582 I am not sure how I should proceed. |
I think it's funny to try to invoke the original constructor when you want to get a mock. I'm going to use sinon's terms from here. Test doubles that testdouble.js creates are meant to be stubs (am I wrong?), but if the original implementation is invoked it's a spy. |
@kethou - no, we definitely do not want to invoke the original constructor. There is a rule in ES class inheritance that requires super to be called in certain cases, however, and I threw that in here when converting from CoffeeScript without thinking about it. |
I'm going to start tracking this in #241 now that we know what's going on |
Thanks for creating such an awesome library! We've been enjoying using it so far.
Is it possible to mock a class with testdouble.js? For instance, let's say I wanted to mock the following module:
I'd like to mock this class like so:
When I run this, I got the following error:
When I try this with an ES5 constructor function, I receive a different error:
Is it possible to accomplish what I'm trying to do? Thanks in advance!
The text was updated successfully, but these errors were encountered: