stalniy/bdd-lazy-var

Does `it` or `its` work with Promise/async `subject`?

newhouse opened this issue · 8 comments

Love the library so far, thanks!

Had a question, though.

It doesn't seem something like this will work:

subject('foo', () => new Promise((res) => setTimeout(() => res('bar'), 1000)))

it(() => is.expected.to.eql('bar'))

I think I can change it to something like this, however:

it('equals bar', async () => expect(await $foo).to.eql('bar'))

but that feels like it defeats the purpose of using subject/it(() => ...).

I also don't want to (I don't think) make this change:

subject('foo', async () => await new Promise(...))

...because at some point I want to have what foo calls actually throw/reject, and to test that I need to not await the resolution first, I think. Since I'll have to do something like this:

it('blah', () => {
  await expect($foo).to.be.rejectedWith('error pattern')
})

Use eventually from chai-as-promised package

I think this is a duplicate of #5

By the way,

subject('foo', async () => await new Promise(...))

Is the same as

subject('foo', () => new Promise(...))

Because async function returns a Promise and it works exactly the same way as Promise.resolve(Promise.resolve(5))

Is the same as

Yeah, my example was a bit over-simplified, but thanks.

OK, so, thank you for the eventually call-out - this makes sense. That actually works for me in the it(...) style (thanks!), but seemed to perhaps reveal a bug in the its(...) style.

Example:

describe('bdd-lazy-var', function () {
  subject('result', () => {
    return new Promise((resolve) => setTimeout(() => resolve('bar'), 1000))
  })

  // Passes successfully
  it(() => {
    is.expected.to.eventually.eql('bar')
  })

  // Errors
  its('result', () => {
    is.expected.to.eventually.eql('bar')
  })
})

Here's the error:

1) bdd
       result
         is expected to eventually eql('bar');:
     TypeError: Cannot read property 'then' of undefined
      at assertIsAboutPromise (node_modules/chai-as-promised/lib/chai-as-promised.js:30:35)
      at Assertion.<anonymous> (node_modules/chai-as-promised/lib/chai-as-promised.js:53:13)
      at Assertion.propertyGetter (node_modules/chai/lib/chai/utils/addProperty.js:62:29)
      at Object.get (<anonymous>)
      at Object.proxyGetter [as get] (node_modules/chai/lib/chai/utils/proxify.js:86:22)
      at Context.its (test/server/lib/api-helpers.test.js:15:27)

FYI - I am using the bdd-lazy-var/getter UI so that I can take sidestep linter complaints using this workaround.

Ok. Yeah. Looks like bug. Will fix it tomorrow. Thanks!

OK, after some investigation I see this is not a bug :) its is a special type of it that puts expectations on property of the subject. In your case its('result', ...) is the same as it('test', () => expect(get.subject.result).to.eventually.eql('bar')). get.subject returns a Promise and that promise's result property is undefined. You add eventually modifier which works only with promises, undefined is not a Promise. That's why you see an error (which by the way comes from chai-as-promised)

So, close this as there is nothing to do from my side

Ohhhhhhhhhhh, I see...I didn't realize that the first/string parameter passed to its was significant, other than for describing the test in the output.

I now see that it will be used to access properties on the subject result, and the issue makes sense. As you said, this will try to get a result property on subject, which is (expectedly) not there.

Yes, this is "case closed". Thank you for your help!