0

Can someone please explain the significance of the location of the "await" keyword in the "expect" function from the waffle testing framework?

for example:

this passes (assume its correct):

expect(await contract.function()).to.equal(something);

this fails (with same input):

await expect(contract.function()).to.equal(something);

this passes (assume its correct):

await expect(contract.function()).to.be.reverted;

this fails (with same input):

expect(await contract.function()).to.be.reverted;

to reproduce:

contract: https://hardhat.org/tutorial/writing-and-compiling-contracts.html

test (under "full coverage"): https://hardhat.org/tutorial/testing-contracts.html

as is, all the tests pass, however when you make any change like above, that particular tests fail.

so my question is, why is it different for to.equal and to.be.reverted? I got an error message saying "TypeError: promise.then is not a function." What does that mean and why are they different? Isnt this something that should be clearly explained in the docs or is it obvious for most people?

Heres an example from https://ethereum-waffle.readthedocs.io/ where "await" is on both sides of "expect": enter image description here

Why is that exactly? how is it different?

Thank you!!!

1 Answer 1

3

The await keyword blocks continuation of execution until a promise has been resolved (or rejected, which throws an Error).

This is nothing specific to waffle, it's a feature of modern JavaScript and was introduced to solve the problem of the "callback hell".

Before await and Promises things looked like this:

contract.function(arg, function callback(err, result) {
  if (err) return cb(err);
  expect(result).to.be.equal.to(something);
  cb();
});

Then, with promises and ES6, things changed to this:

contract.function(arg).catch((err) => {
  cb(err);
}).then((result) => {
  expect(result).to.be.equal.to(something);
  cb();
});

Now with async/await we can get rid of all callbacks:

await expect(await contract.function(arg)).to.be.equal.to(something);

Basically await blocks and wait until the promise returns an result or error. If it's an error, the error is thrown. If it's a result, it's returned like a return value.

This immensely improves readability once you can wrap your head around the concept. There are plenty of resources online with further explanations - as said, look for general JavaScript explanations, since this is not specific to waffle/web3.


I noticed I missed the actual question: Why does .to.equal need await and .to.be.reverted doesn't.

expect(await contract.function()).to.equal(something);

Here you are first awaiting for contract.function() to return a result. Then you want expect to compare that result with something.

await expect(contract.function()).to.be.reverted;

Here you are not checking the result of contract.function() but instead you are having expect check whether the Promise returned ended up being rejected.


Another approach to the answer:

p = contract.function() always returns a Promise, here p. A Promise is basically an object that you can tell that you want to be informed when the result is available. The result of a Promise can either be, that it was Resolved (transaction was successful, maybe with data) or that it was Rejected (transaction failed, maybe with error).

v = await p always takes a Promise, here p. Await takes that promise and makes sure that execution in the current context doesn't continue on until the Promise yielded a result. If the Promise resolves, await returns the data (value v) of the successful transaction. If the Promise was rejected, an Error is thrown with the err data.

expect(v).to.be.equal.to(something); - when comparing two values, you need actual values - not the Promise that there might be a value later. So if the value comes from a Promise, you must first await it.

expect(p).to.be.reverted; - when checking whether a transaction reverted (= Promise rejected) - we can't await for the value v, because we expect there won't be a value, but instead an error. So instead of passing a value we won't get, we instead pass the Promise that is supposed to be rejected.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.