stampit-org / stamp

Stamps - better OOP model
https://stampit.js.org
MIT License
25 stars 3 forks source link

Privatized stamp with stubs/fakes #47

Open MaximusHaximus opened 6 years ago

MaximusHaximus commented 6 years ago

Howdy, I'm running into some problems with stubbing methods on stamps that have been privatized. In particular, because referencing the injected method always references the PROXY function, there's no way to access the stubs properties. It'd be nice to be able to reference the stubs on their location since they are not private-ized and it's counter intuitive to not be able to access properties on the public methods. Not sure the best way to go about achieving that goal. For now I am storing my stubs in a local closure, then referencing them from their local closure variables. It'd be nicer to be able to reference the functions directly on the object. Open to any alternative composition patterns too, if there's a better way to accomplish this.

const Configure = require('@stamp/configure');
const stampit   = require('@stamp/it');
const sinon     = require('sinon');

const exampleStamp = stampit.compose(Configure)
  .methods({ myMethod() { return true; } });

const exampleStampInstance = exampleStamp();

const exampleMethodResult = exampleStampInstance.myMethod();
console.log(exampleMethodResult); // true

const myStub       = sinon.fake.returns(false);
const stubbedStamp = exampleStamp.methods({ myMethod: myStub });

const stubbedStampInstance = stubbedStamp();

const stubbedMethodResult = stubbedStampInstance.myMethod();

console.log(stubbedMethodResult); // false -- fake was called as expected (good!)
console.log('myStub.called', myStub.called); // true -- the stub logged the call

console.log('stubbedStampInstance.myMethod.called', stubbedStampInstance.myMethod.called); // undefined (should be true, not intuitive at all)
koresar commented 6 years ago

Hello there! 👋

For now I'd like to share what happened to me since I started doing stamps.

I used to use sinon a lot. Until I met stamps. Since then I do not use sinon. When my projects are using stamps the sinon is not needed.


There are two quick solutions though. 1) Avoid privatisation by adding .noProvatize():

const exampleStamp = stampit.compose(Configure.noPrivatize())
  .methods({ myMethod() { return true; } });

2) Or do not use sinon:

let called = 0;
exampleStamp.methods({ myMethod() {
  called += 1;
  return false;
} });

const stubbedStampInstance = stubbedStamp();

const stubbedMethodResult = stubbedStampInstance.myMethod();
console.log(called); // 1

If none of the above suffice feel free to ask further.

MaximusHaximus commented 6 years ago

Understood that I could write my own equivalent of Sinon, but then I am re-writing a ton of assertions, especially if I want to assert against calledWith etc.

This is a more general problem with the Privatize stamp in that you cannot access ANY properties on public functions due to the method of proxying, my Sinon use case is just the first one I ran into :)

koresar commented 6 years ago

I hope to rewrite the Privatize when ES2018 Privates is released: https://github.com/tc39/proposal-private-methods#private-methods-and-fields It's gonna be hard though. :)

Deadarius commented 5 years ago

@MaximusHaximus I use next pattern:

P.S. hehe, what an encounter, brother.

koresar commented 5 years ago

Looks like the Privatize can be implemented without a proxy-object. But with proxy-Proxy. 😂 https://medium.com/dailyjs/how-to-use-javascript-proxies-for-fun-and-profit-365579d4a9f8

This would allow privatizing only the necessary properties, not all of them! Also, good for performance constrained applications.