Solidity Contract Debugging – How to Handle Expected Throw in Contract Test Using Truffle and Ethereum-testRPC

contract-debuggingsoliditytestingtestrpctruffle

Is it possible to write a test using truffle that attempts to confirm that a throw occurs in a contract? For example, if I had a contract with function…

contract TestContract {
  function testThrow() {
    throw;
  }
}

if I write a test in truffle that invokes this function, then truffle test basically crashes with:

Error: VM Exception while executing transaction: invalid JUMP

Is there any way to handle this exception from within your test to verify that the throw actually occurred? The reason I want to do so is to test that my functions actually throw when the user passes in invalid input?

Best Answer

You can use OpenZeppelin's expectThrow helper -

Source: https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/test/helpers/expectThrow.js

export default async promise => {
      try {
        await promise;
      } catch (error) {
        // TODO: Check jump destination to destinguish between a throw
        //       and an actual invalid jump.
        const invalidJump = error.message.search('invalid JUMP') >= 0;
        // TODO: When we contract A calls contract B, and B throws, instead
        //       of an 'invalid jump', we get an 'out of gas' error. How do
        //       we distinguish this from an actual out of gas event? (The
        //       testrpc log actually show an 'invalid jump' event.)
        const outOfGas = error.message.search('out of gas') >= 0;
        assert(
          invalidJump || outOfGas,
          "Expected throw, got '" + error + "' instead",
        );
        return;
      }
      assert.fail('Expected throw not received');
    };

I use it my test cases like this -

import expectThrow from './helpers/expectThrow';
.
.
.
describe('borrowBook', function() {
        it("should not allow borrowing book if value send is less than 100", async function() {
            await lms.addBook('a', 'b', 'c', 'e', 'f', 'g');
            await lms.addMember('Michael Scofield', accounts[2], "Ms@gmail.com");
            await lms.borrowBook(1, {from: accounts[2], value: 10**12})
            await expectThrow(lms.borrowBook(1, {from: accounts[2], value: 10000})); // should throw exception
        });
});
Related Topic