I'm using a save
middleware in Mongoose to create a log of activity in the DB whenever some action is taken. Something like
UserSchema.post("save", function (doc) {
mongoose.model("Activity").create({activity: "User created: " + doc._id});
});
This appears to work fine, but the problem is that I can't test it because there is no way to pass a callback to post
(which probably would not make sense). I test this out using mocha
with:
User.create({name: "foo"}, function (err, user) {
Activity.find().exec(function (err, act) {
act[0].activity.should.match(new RegExp(user._id));
done();
});
});
The problem is that the Activity.create
apparently does not finish before .find
is called. I can get around this by wrapping .find
in setTimeout
, but this seems hacky to me. Is there any way to test asynchronous mongoose middleware operations?
Unfortunately, there's not a way to reliably interleave these two asynchronous functions in the way you'd like (as there aren't threads, you can't "pause" execution). They can complete in an inconsistent order, which leaves you to solutions like a timeout.
I'd suggest you wire up an event handler to the Activity class so that when an Activity is written/fails, it looks at a list of queued (hashed?) Activities that should be logged. So, when an activity is created, add to list (
"onactivitycreated")
. Then, it will eventually be written ("onactivitywritten"
), compare and remove successes maybe (not sure what makes sense with mocha). When your tests are complete you could see if the list is empty.You can use
util.inherits(Activity, EventEmitter)
for example to extend the Activity class with event functionality.Now, you'll still need to wait/timeout on the list, if there were failures that weren't handled through events, you'd need to handle that too.
Edit -- Ignore the suggestion below as an interesting demo of async that won't work for you. :)
If you'd like to test them, I'd have a look at a library like async where you can execute your code in a series (or
waterfall
in this case) so that you can first create aUser
, and then, once it completes, verify that the correctActivity
has been recorded. I've usedwaterfall
here so that values can be passed from one task to the next.