The comparison to proxyquire doesn't mention noCallThru
0xR opened this issue · 3 comments
When you look at this https://github.com/thlorenz/proxyquire#preventing-call-thru-to-original-dependency
You can prevent the call thru for the Request.js
from the example. This means no ajax calls will be performed. Doing this I don't see the difference with Mocktail and proxyquire.
proxyquire
is all about monkeypatching, which is a perfectly legitimate approach – it's the same approach that Jasmine's spies with spyOn
takes.
With monkeypatching you're assuming that the items you wish to mock are available on the interface, however if you've abstracted away such details — for example by using a Proxy
or by using the revealing module pattern then monkeypatching becomes unavailable, whereas Mocktail
would continue to function.
I've used proxyquire
myself — in fact a lot more than I've used my own Mocktail
module — and continue to do so. I'm not claiming that Mocktail
is superior in any way, rather it's a different approach to the same problem.
Ok, it would help to make clear this distinction in the readme.
Also explain how you would use a proxy, I don't understand it yet. The revealing module pattern seems unnecessary in es6 in which the examples are written. Maybe give examples on when to use your module and when to use proxyquire.
Ponyfoo is currently doing a great series on ES6 features — take a look at the proxies in depth.
It's not easy for me to sum up succinctly in the README, but I'll try my best in here and then I can link to this issue.
Eventually everything is monkey-patchable in your code, but it's a case of where you do it.
For example, consider the following:
import {makeRequest} from './Request';
export default class DataProcessor {
getData() {
return new Promise(resolve => {
const requests = [makeRequest('/users'), makeRequest('/places')];
Promise.all(requests).then(response => {
// Some logic to handle the response.
// ...
resolve({ users, places });
});
});
}
}
The getData
method is available on the interface, and is therefore capable of being monkey-patched — whereas the makeRequest
method isn't available because it's abstracted away.
spyOn(dataProcessor.getData).and.callFake(() => ...);
With proxyquire
you'd simply mock the getData
method — but for that you'd also need to mock out all of the logic in the then
callback. This is definitely one approach, and from my point of view is neither good nor bad — it gets the job done, and your code is — most importantly — tested. You'd also be wise to abstract away the logic as well, which could be testable in isolation provided it's available on the interface.
With Mocktail
you'd instead add the mock
method when you export your DataProcessor
object (as I see it, a downside to Mocktail
and I'd rather have a way of adding middleware to an import
— think — God forbid — PHP's __autoload
hook).
When running the getData
method in production, the live makeRequest
method would be used and AJAX requests would be sent to the server. However, when creating your unit-tests you'd configure the makeRequest
method to instead use your mocked adaptation – which would presumably use setTimeout
with some arbitrary timeout value.
Therein lies the advantage of Mocktail
over proxyquire
in this instance (I still maintain that proxyquire
is a more robust solution). With Mocktail
the makeRequest
method is a fake, and therefore by invoking getData
no AJAX requests are made. In a nutshell, you've monkey-patched the un-monkey-patchable by instead injecting a fake object into your live code — think Angular-esque dependency injection.
With the Mocktail
approach you can now go ahead and invoke getData
without duplicating its logic for your unit-tests. All logic remains centralised, and modifying your getData
logic does not mean changing your getDataMock
logic, because there is no such method — only a fake makeRequest
.