2

I'm trying to write tests for contracts using truffle; I have opened an issue here. The workflow is as follows:

mkdir truffle_testing
truffle init
rm -r tests/* contracts/* build/*

Next, I create the following contract files in directory contracts/ :

C.sol

contract C {

    string words = "Bla bla bla";

    uint256 lastVal;

    function math(uint256 a, uint256 b) {
        lastVal = a + b;
    }

    function getVal() constant returns (uint256) {
    return lastVal;
    }

    function getWords() constant returns (string) {
        return words;
    }
}

and

Migrations.sol

contract Migrations {
  address public owner;

  // A function with the signature `last_completed_migration()`, returning a uint, is required.
  uint public last_completed_migration;

  modifier restricted() {
    if (msg.sender == owner) _
  }

  function Migrations() {
    owner = msg.sender;
  }

  // A function with the signature `setCompleted(uint)` is required.
  function setCompleted(uint completed) restricted {
    last_completed_migration = completed;
  }

  function upgrade(address new_address) restricted {
    Migrations upgraded = Migrations(new_address);
    upgraded.setCompleted(last_completed_migration);
  }
}

Then I create a test suite in the tests/ directory:

contract('C', function(accounts) {
  it("Getting the words string from the contract", function() {
    var c = C.deployed();

    return c.getWords.call(accounts[0]).then(function(words) {
      assert.equal(words, "Bla bla bla", "[E] The contract should have said \"Bla bla bla\"");
    });
  });

  it(" 2 + 2 = 4 ", function(){
    var c = C.deployed();
    // Call method to initialize value (this method only returns the tx id and not the
    // actual value)
    c.math(2, 2, {from:accounts[0],gas:3000000}).then(function(tx_that_we_dont_need) {    
        return c.getVal.call().then(function(additionResult){
            assert.equal(additionResult, 4, "[E] 2 + 2 = 4 even in eth. contracts.");
       });
    });
  });
});

After all files have been written I launch testrpc:

testrpc:

EthereumJS TestRPC v2.1.0

Available Accounts
==================
...

then build the project:

truffle compile --compile-all
truffle migrate
truffle test

As expected, all tests pass but they also pass if I change the value in the second test from 4 to any other integer value.

Why?

Edit:

I've added the done callback to my tests but it doesn't work (tests still show up as passing when they shouldn't):

contract('C', function(accounts) {
  it("Getting the words string from the contract", function() {
    var c = C.deployed();

    return c.getWords.call(accounts[0]).then(function(words) {
      assert.equal(words, "Bla bla bla", "[E] The contract should have said \"Bla bla bla\"");
    });
  });

  it(" 2 + 2 = 4 ", function(){
    var c = C.deployed();
    // Call method to initialize value (this method only returns the tx id and not the
    // actual value)
    c.math(2, 2, {from:accounts[0],gas:3000000}).then(function() {    
        return c.getVal.call().then(function(additionResult){
            assert.equal(additionResult, 6, "[E] 2 + 2 = 4 even in eth. contracts.");
        done();
       }).done(null, function(error) {
        done(error); 
       });
    });
  });

});

Edit #2:

I have tried running the following code:

contract('C', function(accounts) {
  it("Getting the words string from the contract", function() {
    var c = C.deployed();

    return c.getWords.call(accounts[0]).then(function(words) {
      assert.equal(words, "Bla bla bla", "[E] The contract should have said \"Bla bla bla\"");
    });
  });

  it(" 2 + 2 = 4 ", function(){
    var c = C.deployed();
    // Call method to initialize value (this method only returns the tx id and not the
    // actual value)
    c.math(2, 2, {from:accounts[0],gas:3000000}).then(function() {    
        return c.getVal.call().then(function(additionResult){
            assert.equal(additionResult, 6, "[E] 2 + 2 = 4 even in eth. contracts.");
        done();
       }).done(null, function(error) {
        done(error); 
       });
    });
  });

});

but now get a timeout error:

Contract: C
    ✓ Getting the words string from the contract (232ms)


    1)  2 + 2 = 4 
    > No events were emitted


  1 passing (5m)
  1 failing

  1) Contract: C  2 + 2 = 4 :
     Error: timeout of 300000ms exceeded. Ensure the done() callback is being called in this test.
5

I think your issue is related on how mocha works with asynchronous code: you must pass a done() function to the it function and execute it at the end of your test, so mocha waits until it is called to perform the test. You have examples in the metacoin.js test file that ships with truffle 1 (don't know how v.2 is shipped).

So, in your test, you should do:

it("Getting the words string from the contract", function(done) {
var c = C.deployed();

return c.getWords.call(accounts[0]).then(function(words) {
  assert.equal(words, "Bla bla bla", "[E] The contract should have said \"Bla bla bla\"");
})
.then(() => {done()})
.catch(done)
;
  • I have tried to use the done callback and the tests still pass (see the edits in the question). – Sebi Aug 8 '16 at 8:08
  • Do you know of a minimal example that performs a state change call on the contract (strangely, the first test passes and fails correctly). – Sebi Aug 8 '16 at 8:13
  • 1
    In your edit, you didn't paste the code well. done is a function that must be passed to the it() method as parameter. Then it must be invoked inside a .then(). You must pass done as a parameter to it in order to force mocha to wait to done() execution to perform the test. If not, it just treat your it() functions as empty because the code inside them is asynchronous. Later I paste an example on mocha tests performing contracts state changes. – Sergeon Aug 8 '16 at 9:43
  • Ok, thanks. I've tried the suggestion and got a time out error (I've updated the question in the second edit). – Sebi Aug 8 '16 at 11:26
  • 2
    I load up an example I was working on: github.com/Sergeon/ethereum-truffle-solidity-ballot-contract However is truffle v.1, I hope it helps. – Sergeon Aug 8 '16 at 21:37

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.