chaijs / chai-spies

Spies for Chai Assertion Library.
MIT License
132 stars 29 forks source link

expect(spy).to.have.been.called.with always true #82

Open xavicolomer opened 7 years ago

xavicolomer commented 7 years ago

"chai": "^4.1.2", "chai-as-promised": "^7.1.1", "chai-spies": "^0.7.1",

Hi there,

I am testing a little ORM I created myself

I am having some problems testing the result of a spy on a promise, the test always passes, no matter what I put inside with, in the example below I just put null, although query should be called with a SELECT SQL sentence

Any suggestions?

it('test SQL',
    () => {
      Item.database = { query: queryReturnsInsert };

      const item = new Item();
      item.name = 'James';

      const spy = chai.spy.on(Item.database, 'query');

      item.save({ in: 'go' })
        .then(function() {
          expect(spy).to.have.been.called.with(null);
        });
    });
vieiralucas commented 7 years ago

I think you need to return the Promise so the test runner can wait on it. It looks like the assertion never runs. Try:

it('test SQL', () => {
  Item.database = { query: queryReturnsInsert };

  const item = new Item();
  item.name = 'James';

  const spy = chai.spy.on(Item.database, 'query');

  return item.save({ in: 'go' })
    .then(function() {
      expect(spy).to.have.been.called.with(null);
    });
});
xavicolomer commented 7 years ago

@vieiralucas You are right, but now I get this:

AssertionError: expected { Spy, 1 call } to have been called with [ Array(3) ]

After changing it the last code to what should return

it('test SQL',
    () => {
      Item.database = { query: queryReturnsInsert };

      const item = new Item();
      item.name = 'James';

      const spy = chai.spy.on(Item.database, 'query');

      return item.save({ in: 'go' })
        .then(function() {
          expect(spy).to.have.been.called.with(`INSERT INTO items
              (\`name\`)
              VALUES(?)`, 'go', [item.name]);
        });
    });
vieiralucas commented 7 years ago

@xavicolomer are you sure that item.save is calling Item.database.query ? Can you provide a more detailed example? Maybe a repo?

xavicolomer commented 7 years ago

Yes it is called.

Please find below an extended version of the code above with the function. The queryReturnsInsert is logged correctly.

I also tried adding eventually (although since its inside then its already resolved)

it('test SQL',
    () => {
      const queryReturnsInsert = (arg1, arg2, arg3) => {
        console.log('Query arguments: ', arg1, arg2, arg3);
        return Promise.resolve({ insertId: 0 })
      };

      Item.database = { query: queryReturnsInsert };

      const item = new Item();
      item.name = 'James';

      const spy = chai.spy.on(Item.database, 'query');

      return item.save({ in: 'go' })
        .then(function() {
          expect(spy).to.have.been.called.with(`INSERT INTO items
              (\`name\`)
              VALUES(?)`, 'go', [item.name]);
        });
    });

then I get this error:

TypeError: { [Function]
  toString: [Function: toString],
  reset: [Function],
  __spy: { calls: [ [Object] ], called: true, name: undefined } } is not a thenable.
xavicolomer commented 7 years ago

Since the database.query its abstract from the ORM, I can also simply kind of mock and save the arguments:

let _sql, _country, _values;

const queryReturnsInsert = (SQL, country, values) => {
  _sql = SQL;
  _country = country;
  _values = values;
  return Promise.resolve({ insertId: 0 })
};

And then:

expect(_sql).to.equal(`INSERT INTO items
              (\`custom_name\`)
              VALUES(?)`);

Although its not really clean

stalniy commented 6 years ago

In order to check precise order of arguments you need to use .called.with.exactly()