Skip to content
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

Stubbing priority #280

Closed
edenworky opened this issue Aug 10, 2017 · 2 comments
Closed

Stubbing priority #280

edenworky opened this issue Aug 10, 2017 · 2 comments

Comments

@edenworky
Copy link

I'm stubbing a function so that if it's called with anything but the specified arguments, it should throw.

const func = td.function()
td.when(func(1, 2)).thenReturn()
td.when(called with something else).thenThrow()

I tried throwing for anything else like so: td.when(func(), {ignoreExtraArgs: true)). In this, case the more general stubbing takes precedence and an error is thrown every time.

I also tried using td.matchers.not() like so: td.when(func(td.matchers.not(1), td.matchers.not(2))). In this case, it only throws when both of the arguments are incorrect.

func('foo', 'bar') // Throws
func(1, 99) // Doesn't throw, but should

Is there a way to write a stub with the behavior I want?

@searls
Copy link
Member

searls commented Aug 10, 2017

This is possible, but since the more-detailed stub satisfies the earlier one, which immediately throws, you need to do this hacky try-catch situation.

// Responding to https://github.com/testdouble/testdouble.js/issues/280
//
// This is necessary, because to set up a subsequent stub that would also satisfy an earlier
// stubbing which would throw an error, the test double would throw and `td.when` would never
// be called.
// What's actually happening is `func(1,2)` is called, that call is stored, then td throws.
// Finally, when `td.when` is called in the catch, it'll pop that most-recent func(1,2) off td's
// internal call store, and set up the more-detailed stubbing

const td = require('testdouble')

const func = td.function()
td.when(func(), {ignoreExtraArgs: true}).thenThrow(new Error('ohai'))
try { func(1, 2) } catch(e) { td.when(/* yuck */).thenReturn('yay') }

func(1,2)

Here it is running on Runkit

@searls searls closed this as completed Aug 10, 2017
@searls
Copy link
Member

searls commented Aug 10, 2017

One last note on intention: I would generally never do this in an actual unit test. If the test has proper control, then {ignoreExtraArgs: …} catch-alls should almost never be necessary, since the unit test should be fully aware of everything the function will actually be called with.

That is, if you're trying to exercise the error case in a test then I would stub explicitly and exactly the args being passed to the double in that case, like this:

td.when(func(3,4)).thenThrow(new Error('nuh-uh!'))
var error
try { 
  subject()
} catch (e) { 
  error = e
}

assert(erorr.message, 'Nuh-uh!')

In fact, it's pretty rare for me to even need to stub the same double twice, much less once with a catch-all.

This may be an example of the library not minding that it's making what you're trying to do painful, as it would be a bad idea for an isolated unit test the majority of the time. (I can't think of an exception, but I'd be all ears if you have an interesting test case)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants