2

Consider the following Truffle Unit Test written in JavaScript:

const Lease = artifacts.require("./Lease.sol");                                                     
contract("Lease", async (accounts) => {                                                             
    it("Rinkeby delay problem", async () => {                                                       
    let instance = await Lease.deployed();                                                      



    // let's check the initial state                                                            
    let state0 = await instance.tenantState();                                                  
    console.log("initial state: " + state0);                                                    



    // let's change the state                                                                   
    let tx = {from:accounts[2], to:instance.address, value:2000};                               
    web3.eth.sendTransaction(tx);                                                               
    await instance.updateTenantState();                                                         



    // let's check if the state changed                                                         
    let state1 = await instance.tenantState();                                                  
    console.log("resulting state: " + state1);                                                  



    // let's wait for one second and check the state again                                      
    setTimeout(async () => {                                                                    
        await instance.updateTenantState();                                                     
        let state2 = await instance.tenantState();                                              
        console.log("resulting state after 1 second: " + state2);                               
    }, 1000);                                                                                   



    });                                                                                             
});

When I run this test on my GanacheUI instance with zero block delays, I get the following results:

$ truffle test test/delay.test.js --network dev
Using network 'dev'.

Compiling ./contracts/Lease.sol...
Compiling ./contracts/LeaseMock.sol...
Compiling ./contracts/Logic.sol...


  Contract: Lease
initial state: 1
resulting state: 0
    ✓ Delay problem (196ms)


  1 passing (226ms)

resulting state after 1 second: 0

However, when I add a 15 second block delay in GanacheUI (which is about the same block delay as the mainnet), I get this:

$ truffle test test/delay.test.js --network dev
Using network 'dev'.

Compiling ./contracts/Lease.sol...
Compiling ./contracts/LeaseMock.sol...
Compiling ./contracts/Logic.sol...


  Contract: Lease
initial state: 1
resulting state: 1
    ✓ Delay problem (30212ms)


  1 passing (30s)

resulting state after 1 second: 0

This has disastrous effects on my unit tests: around 70% of them fail with very diverse errors.

I got similar results with AragonOS's repository using GanacheUI: without block delays all test pass, but with a 15 second block delay many fail. (They have an option to test on Ropsten, Kovan and Rinkeby using Infura, but I didn't try that).

So what are the best practices? Should I ignore block delays altogether? In that case, how am I supposed to test my contracts on the testnets? Or should I design my tests so that they handle block delays properly? If so, how? And finally: are there security implications of ignoring block delays in unit tests?

1

It is better if you design your tests such that they do not depend on the delay between blocks. In a real network blocks are generated every 15 seconds only on average and they can take longer or shorter intervals. Also in the future the network average can be lowered.

In your unit test the problem is you are polling for the state of your contract to change. You can't be sure when to stop polling, in private test network waiting 30 seconds should be enough, in little used network like rinkeby perhaps a couple of minutes will do, but in a busy network like mainnet 10 minutes might not be sufficient.

A better approach here is to generate an event with your transaction and make your test wait for that event to be triggered. You have to make sure the gas price used will allow the transactions to be processed with little delay in a public network.

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.