Nubescope / sinon-mongoose

Extend Sinon stubs for Mongoose methods to test chained methods easily
MIT License
87 stars 29 forks source link

count() and lean() support #1

Closed AoDev closed 8 years ago

AoDev commented 8 years ago

Hello,

I have this kind of query: (note it's with co and generators)

var item = yield Model
    .findOne({ id: id })
    .sort({ createdAt: -1 })
    .lean()

or

... setup params
  var result = yield {
    count: Model.find(findParams).count(),
    items: Model.find(findParams).sort(sort).limit(limit).skip(limit * page).lean()
  }

I am a bit new to testing mongoose this way, but currently I have count is not a function or ...lean is not a function.

gaguirre commented 8 years ago

As far i know, sinon does not support generators. However, you can try with sinon-es6, a fork of sinon with es6 support.

Tell me if it works, so can close this issue :)

AoDev commented 8 years ago

The issue is not the generator, I mentioned it because I did not want to rewrite my snippet. I don't have time to make a test case right now, but if you don't have time I'll do it later. :)

If you do have time, can you add lean and count in your tests and see if they work or fail?

gaguirre commented 8 years ago

Done! I added two tests (for lean and count methods). Tell me if I'm missing something.

AoDev commented 8 years ago

@gaguirre Thank you ! I was able to make it run. So, lean and count were working actually. (But it is still good to include them in the tests :P)

But, I am in some "edge case" maybe although I have seen more people doing the same. Here is what confused me, but makes sense somehow:

In our API endpoint, we have a Model.find query. But, we use it twice:

It looks like this: (we use Koa with generators)

  // setting all the sort, limit, findParams before
  ...
  var q = yield {
    count: Model.find(findParams).count(),
    items: Model.find(findParams).sort(sort).limit(limit).skip(limit * page).lean()
  }

For my test to pass I had to include both "find":

ModelMock
  .expects('find').withArgs({ account: testAccount._id })
  .chain('count')
  .resolves(10)

ModelMock
  .expects('find').withArgs({ account: testAccount._id })
  .chain('sort', '-lastOccurrence')
  .chain('limit', 50)
  .chain('skip', 0)
  .chain('lean')
  .resolves([])

yield request(server.listen())
  .get('/account/' + testAccount.id + '/trace')
  .set('Authorization', 'Bearer ' + user.getToken())
  .expect(200)
  .end()

ModelMock.verify()

The thing is, at first I only tested the "search for items" part, without the "count" part. This would throw an error. It works only if I add both. The error output is not clear because it does display something like:

 GET /endpoint
{ [ExpectationError: Unexpected call: find({ account: 565754c49b4fe1f405750dc0 })
    Expectation met: find({ account: 565754c49b4fe1f405750dc0 }[, ...]) once] name: 'ExpectationError' }
ExpectationError: Unexpected call: find({ account: 565754c49b4fe1f405750dc0 })
    Expectation met: find({ account: 565754c49b4fe1f405750dc0 }[, ...])
...
super
long
stacktrace
...

So it is confusing to see the expected call with the right parameters displayed, but failing.

It makes sense that in such case one query is solved but not the other. But to be honest it feels magical as how does sinon-mongoose does verify and expects both requests in this manner.

gaguirre commented 8 years ago

Good to hear you solved it! You're right it's confusing in some way, but the behavior is similiar to others on sinon.

For example, on parameters expectation. If you have this code

Model.find({ name: 'Joe' }).exec()

This two expectations will be ok, despite the second didn't check the parameters

sinon.mock(Model)
  .expects('find')
  .withArgs({ name: 'Joe' })  
  .chain('exec')

sinon.mock(Model)
  .expects('find')
  .chain('exec')

However, I'm gonna think if there is a way to check chained methods more strictly ;)

AoDev commented 8 years ago

I am closing the issue. Thank you for your time, and your module : )