-
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
How to Stub a Chained Function #360
Comments
So, first, to answer your question, this is how that can be achieved. I just tested this locally function subject (googleMapsClient) {
return googleMapsClient.placesNearby({
location: 'some coords',
radius: 42,
keyword: 'tacos'
}).asPromise()
}
const td = require("./lib");
const assert = require('assert')
async function test () {
// Smell 1: dependency takes two steps for setup, good deps don't
const googleMaps = require('@google/maps')
const googleMapsClient = googleMaps.createClient()
// Smell 2: mocking a third-party library. Any pain experienced is useless pain
td.replace(googleMapsClient, 'placesNearby')
// Smell 3: stubs-returning stubs. A good dep should do its job in one step
td.when(googleMapsClient.placesNearby({
location: 'some coords',
radius: 42,
keyword: 'tacos'
})).thenReturn({
asPromise: td.when(td.func('asPromise')()).thenResolve('some places')
})
const result = await subject(googleMapsClient)
assert.equal(result, 'some places')
}
test() However, I think this is a good test case for why you shouldn't mock third-party dependencies. I talk about this at greater length in my recent talk about mocking smells, too. Instead, what I recommend is creating a wrapper of the third-party library's functionality you want and mocking that. So in this case that might look like: // wrap/maps.js
const googleMaps = require('@google/maps')
module.exports = {
placesNearby: function (options) {
const googleMapsClient = googleMaps.createClient({key: process.env['GOOGLE_API_KEY']})
return googleMapsClient.placesNearby(options).asPromise()
}
} // subject.js
const maps = require('./wrap/maps')
module.exports = function subject (options) {
return maps.placesNearby(options)
} //test.js
const td = require("./lib");
const assert = require('assert')
async function test () {
const maps = td.replace('./wrap/maps')
const subject = require('./subject')
const options = {
location: 'some coords',
radius: 42,
keyword: 'tacos'
}
td.when(maps.placesNearby(options)).thenResolve('some places')
const result = await subject(options)
assert.equal(result, 'some places')
}
test() Now the contracts are clear and nothing was hard or awkward to set up. You'll also find yourself building an explicit contract in that |
Thanks for the full run down @searls. Completely agree with the wrapper concept. That said, occasionally I do still find myself wanting to mock a chained API. For better or worse, I put together a little helper util, td-chain, to make that process a little more succinct. const chain = require('td-chain')
const opts = {
location: 'some coords',
radius: 42,
keyword: 'tacos'
}
td.when(
chain(googlePlacesClient.placesNearby)(opts).asPromise()
// for comparison, the real call: googlePlacesClient.placesNearby(opts).asPromise()
).thenResolve('some places') |
Description
googleMaps functions can support promise by adding .AsPromise() for example:
Issue
this how the function I want to stub look like
And this is how I try to stub it
I get the following Error
TypeError: googleMaps.placesNearby(...).asPromise is not a function
adding .asPromise() inside td.when, won't resolve the issue and give different error message
And I can't stub googleMaps.placesNearby because it is a function and not an object.
Environment
node -v
output:npm -v
(oryarn --version
) output:npm ls testdouble
The issue can be recreated by installing GoogleMap package and try to stub any function as promise
The text was updated successfully, but these errors were encountered: