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

Is it possible to test middleware #22

Open
newmanw opened this issue May 17, 2017 · 10 comments
Open

Is it possible to test middleware #22

newmanw opened this issue May 17, 2017 · 10 comments
Labels

Comments

@newmanw
Copy link

newmanw commented May 17, 2017

Examples are stubbing out the entire mongoose model and seem to be preventing middleware from being called. Is it possible to test mongoose middleware while mocking?

SomeSchema.pre('save', function(next) {
  // doing some pre save stuff here that I would like to test
});
@gaguirre
Copy link
Collaborator

Do you mean that mocking the save method will prevent the SomeSchema.pre('save', ...) hook to be called?

@newmanw
Copy link
Author

newmanw commented May 17, 2017

@gaguirre yes as far as I can tell. That being said I could be mocking the save wrong. ;) Is this something that should work if I am mocking save correctly?

@gaguirre
Copy link
Collaborator

Probably you are doing it well.

The thing is: if you're mocking the save method then it will not be called, so probably the pre-save hook is not called neither.
I'm wondering if it's something we should solve in sinon-mongoose or just provide a recipe for this case.

@jniemin did you face this issue?

@newmanw
Copy link
Author

newmanw commented May 17, 2017

@gaguirre I think solving in sinon-mongoose or a recipe are both valid options IMHO.

I use the mongoose middleware a lot and think its a great way to separate concerns. That being said I have found it very hard to test that middleware. I have tried to just mock the mongoose collection object directly, which in turn really just mocks the mongodb driver. It works in a sense that all mongoose middleware is run when I do this. However I am at the mercy of figuring out what type of object the mongodb driver returns back to mongoose so things still 'work'.

That being said, I am also looking at seeing if I can access the middleware 'hooks' in my tests directly. That way I can test the middleware function directly rather than indirectly through a save/update/remove call.

@jniemin
Copy link
Contributor

jniemin commented May 18, 2017

I didn't run in to it. But if you think about it, you should not need to test if middleware is called in your test. That is basically testing the mongoose logic, not your code. What I would do instead is test the function passed to middleware

var saveFunction = function(next){
  next();
}

SomeSchema.pre('save', saveFunction);

unit test

var nextSpy = sinon.spy();
saveFunction(nextSpy);
assertTrue(nextSpy.called);

@gaguirre
Copy link
Collaborator

gaguirre commented May 18, 2017

As @jniemin said, I think you can unit test the pre-save hook separately.
A test mocking the save method, returning the mongoose instance as you expect to be modified by the pre-save hook.
Another test just for the hook function.

@newmanw tell me if that's clear enough

And of course, if you find a way to do it right, please open a PR with a recipe for that ;)

@newmanw
Copy link
Author

newmanw commented May 18, 2017

@gaguirre yes, thanks. Only thing that I have trouble with is testing a non exported function.

IE in @jniemin example for that to work I believe you would need to do something like this:

In model.js file

var saveFunction = function(next){
  next();
}

SomeSchema.pre('save', saveFunction);

exports.saveFunction = saveFunction;

Then in test file

var saveFunction = require('./model').saveFunction;
var nextSpy = sinon.spy();
saveFunction(nextSpy);
assertTrue(nextSpy.called);

Not really optimal. I could possibly get around this using rewire. But no good way IMHO to ask a mongoose model for its 'installed' hooks.

@gaguirre
Copy link
Collaborator

@newmanw I think it has to be available somewhere in the schema. If you can find where it is then you can test it, and avoid exporting the method in your model.
If you find a better solution post it here! :)

@jniemin
Copy link
Contributor

jniemin commented May 19, 2017

What I sometime do is assign them to object that is clearly marked private. There is not really optimal way of exposing "private" function out of modules. So exposing private object at least makes it explicit that it is a private object. Not perfect anyway.


var aFunction = function(){}

var privateAPI = {
 aFunction : aFunction
}

module.exports.privateAPI = privateAPI

Then you can call it on tests like

var myModule = require('mymodule');
myModule.privateAPI.aFunction();

@vikpe
Copy link

vikpe commented Oct 9, 2017

@newmanw I stumbled upon the same issue recently and "solved" it by exposing the schema middleware function and using lodash.bind() during testing in order to rewrite the this context.

I made a tiny example repo with source/tests using this approach.

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

No branches or pull requests

4 participants