0

I am trying to test my contracts using the Truffle suite, along with Ganache.

Up until now, I have been testing contract functions which are restricted to view only. Now, when I attempt to make changes to the contract state, I'm finding that the state is not actually being changed between function calls.

Consider the following simple contract:

pragma solidity ^0.4.24;

import "../OrderBook.sol";

contract IdManager {
    uint256 newId;

    function addId() public returns (uint256) {
        newId++;
        return newId;
    }
}

All I wish to do is increment newId and return its new value.

The code I have written to test this is:

const { getWeb3, getContractInstance, parseSignature } = require("./test_helper");
const web3 = getWeb3();
const getInstance = getContractInstance(web3);

contract('IdManager', (accounts) => {
    let IdManager = getInstance('IdManager');

    it('test addId()', async () => {
        console.log(await IdManager.methods.addId().call());
        console.log(await IdManager.methods.addId().call());
        console.log(await IdManager.methods.addId().call());
    });
});

The getInstance() call is making use of Web3 v1.0, based off this tutorial.

With the above test, I would expect the output to be:

1
2
3

But, I actually get:

1
1
1

Has anyone come across this issue before?


Edit0:

console.log(await IdManager.methods.newId().call()); should have been console.log(await IdManager.methods.addId().call());newId() is now addId().


Edit1:

Taking goodvibration's advice, I have modified the example contract to be as follows:

contract IdManager {
    uint256 public newId;

    function addId() public {
        newId++;
    }

    function getId() public view returns (uint256) {
        return newId;
    }
}

The test code has been altered to the following:

it('test addId()', async () => {
    await IdManager.methods.addId().call();
    console.log(await IdManager.methods.getId().call());

    await IdManager.methods.addId().call();
    console.log(await IdManager.methods.getId().call());

    await IdManager.methods.addId().call();
    console.log(await IdManager.methods.getId().call());
});

Unfortunately, my output is still showing that the state is not being preserved:

0
0
0
  • You're not calling addId anywhere in your test!!!!! – goodvibration Sep 17 '18 at 14:40
  • I have updated my question, newId() should have been addId(). Apologies. – Craig Sep 17 '18 at 14:45
  • What is OrderBook??? Is that possibly the old contract which you were trying to run this test with? – goodvibration Sep 17 '18 at 15:03
  • Sorry, I'm not having a very good day. I'm pulling from the wrong set of tests there! I have updated my question again. – Craig Sep 17 '18 at 15:05
1

A simple solution to this was right under my nose. Obviously, I just had to read the documentation given to me.

methods.myMethod.call

Note calling can not alter the smart contract state.

methods.myMethod.send

Note this can alter the smart contract state.

So, the alteration to the last passage of test code in my question is as follows:

it('test addId()', async () => {
    await IdManager.methods.addId().send({from: accounts[0]});
    console.log(await IdManager.methods.getId().call());

    await IdManager.methods.addId().send({from: accounts[0]});
    console.log(await IdManager.methods.getId().call());

    await IdManager.methods.addId().send({from: accounts[0]});
    console.log(await IdManager.methods.getId().call());
});

Which results in my desired output:

1
2
3
0

Function addId is a non-constant function (i.e., not declared as pure or view).

As such, its return-value can be used only in the on-chain, i.e., by other functions (in the same contract or in other contracts) which call it.

When you call such function from the off-chain, then a transaction is executed in the on-chain, and what you get in return is a receipt of that transaction.

If you want the actual return-value, then you need to emit it in an event, which you will later be able to extract from the receipt.

For example:

event Event(uint256 val);
function addId() public returns (uint256) {
    newId++;
    emit Event(newId);
    return newId;
}

Alternatively, since in your specific case you are returning the value of a global (state) variable, you can declare it public, and then simply read it from the contract using its (implicit) getter function:

console.log(await IdManager.methods.newId().call());
  • I should have remembered that executing such functions only returns the Tx receipt, from when I was solely using Web3 and remix! But, I have altered my test code to retrieve the member Id via a separate function call. I am still not seeing a change in state between calls. – Craig Sep 17 '18 at 15:01

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.