2

I have contract with methods that check input parameters using require(bool) so if at least on of conditions does not matched it throws an error. During test procedure (using Truffle and Chaijs) it looks like:

 Error: VM Exception while processing transaction: invalid opcode
  at Object.InvalidResponse (/usr/lib/node_modules/truffle/build/cli.bundled.js:37022:16)
  at /usr/lib/node_modules/truffle/build/cli.bundled.js:210096:36
  at XMLHttpRequest.request.onreadystatechange (/usr/lib/node_modules/truffle/build/cli.bundled.js:208875:13)
  at XMLHttpRequestEventTarget.dispatchEvent (/usr/lib/node_modules/truffle/build/cli.bundled.js:210748:18)
  at XMLHttpRequest._setReadyState (/usr/lib/node_modules/truffle/build/cli.bundled.js:211038:12)
  at XMLHttpRequest._onHttpResponseEnd (/usr/lib/node_modules/truffle/build/cli.bundled.js:211193:12)
  at IncomingMessage.<anonymous> (/usr/lib/node_modules/truffle/build/cli.bundled.js:211153:24)
  at endReadableNT (_stream_readable.js:1047:12)
  at _combinedTickCallback (internal/process/next_tick.js:102:11)
  at process._tickCallback (internal/process/next_tick.js:161:9)

Test uses promises like this:

return MyToken.deployed().then(inst => {
  instance = inst;
  return instance.purchase({from: accounts[1], value: amount});
})....

Problem is that I cannot handle this exception. I tried to wrap purchase call into function and use assert.throws(executor, message) - it does not works (suppose due it expects something synchronous).

How this promise can be tested (preferably without adding chai-as-promised)?

2
  • Promises has a catch that gets called when the promise fails. See this stackoverflow.com/questions/26571328/… .
    – Ismael
    Commented Oct 5, 2017 at 16:23
  • @Ismael it does not works in tests as you expecting (I mean using Chaijs assert.throws).
    – Alex G.P.
    Commented Oct 13, 2017 at 12:05

2 Answers 2

3

You can catch a promise when it was rejected before further processing. Catch doesn'y work in the example because it will catch any other error not only the transaction failure.

You can use the second parameter to then to catch a promise being rejected before further processing, ie MyPromise.then(() => { /* check when succeed */ }, () => { /* check when failure */ })

contract('MyToken', (accounts) => {
  it("Should fail", () => {
    return MyToken.deployed().then((instance) => {
      return instance.purchase({ from: accounts[1], value: amount });
    }).then(() => {
      assert.ok(false, "It didn't fail");
    }, () => {
      assert.ok(true, "Passed");
    });
  });
});

Promises do not throws that is why assert.throws fails.

Note after Byzantium: Also I should mention that after the Byzantium fork some operations that previously generated a throw will no longer do such thing. Check this post so you can update your test for that fact: How do I detect a failed transaction after the Byzantium fork as the REVERT opcode does not consume all gas?


Perhaps a better approach is using async/await, and assert.throws.

The trick is convert the promise to something not async that assert.throws can report. Firs a helper function

async function verifyThrows(pred, message) {
  let e;
  try {
    await pred();
  } catch (ex) {
    e = ex;
  }
  assert.throws(() => {
    if (e) { throw e; }
  }, message);
}

And we can use the helper function passing the promise execution in a function

contract('MyToken', (accounts) => {
  it("Should fail", async () => {
    const instance = await MyToken.deployed();
    await verifyThrows(() => {
      instance.purchase({ from: accounts[1], value: amount });
    }, /revert/);
  })
});
2
  • It does not works. First assert throws an error that caught in catch section.
    – Alex G.P.
    Commented Feb 14, 2018 at 20:53
  • 1
    @AlexG.P. You are correct. It should be fixed now.
    – Ismael
    Commented Feb 15, 2018 at 6:06
0

You can also use my truffle-assertions library to test Solidity function errors, such as invalid opcode errors or reverts.

It can be installed with npm:

npm install truffle-assertions

And then imported at the top of your file:

const truffleAssert = require('truffle-assertions');

You can then use it inside your test file like this:

return MyToken.deployed().then(inst => {
    instance = inst;
    truffleAssert.fails(
        instance.purchase({ from: accounts[1], value: amount }),
        truffleAssert.ErrorType.INVALID_OPCODE
    );
});

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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