Probando contratos con trufa

Estoy tratando de escribir pruebas para contratos usando trufa; He abierto un tema aquí . El flujo de trabajo es el siguiente:

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

A continuación, creo los siguientes archivos de contrato en el directorio 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;
    }
}

y

Migraciones.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);
  }
}

Luego creo un conjunto de pruebas en el directorio tests/ :

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.");
       });
    });
  });
});

Después de escribir todos los archivos, lanzo testrpc :

testrpc:

EthereumJS TestRPC v2.1.0

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

luego construye el proyecto:

truffle compile --compile-all
truffle migrate
truffle test

Como era de esperar, todas las pruebas pasan pero también pasan si cambio el valor en la segunda prueba de 4 a cualquier otro valor entero.

¿Por qué?

Editar:

Agregué la devolución de llamada realizada a mis pruebas, pero no funciona (las pruebas aún se muestran como aprobadas cuando no deberían):

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); 
       });
    });
  });

});

Editar #2:

He intentado ejecutar el siguiente código:

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); 
       });
    });
  });

});

pero ahora aparece un error de tiempo de espera:

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.

Respuestas (1)

Creo que su problema está relacionado con cómo funciona mocha con código asíncrono : debe pasar una función done() a la función it y ejecutarla al final de su prueba, por lo que mocha espera hasta que se llame para realizar la prueba. Tiene ejemplos en el archivo de prueba metacoin.js que se envía con truffle 1 (no sé cómo se envía v.2).

Entonces, en tu prueba, debes hacer:

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)
;
He intentado usar la devolución de llamada realizada y las pruebas aún pasan (ver las ediciones en la pregunta).
¿Conoce algún ejemplo mínimo que realice una llamada de cambio de estado en el contrato (curiosamente, la primera prueba pasa y falla correctamente).
En su edición, no pegó bien el código. done es una función que debe pasarse al método it() como parámetro. Luego debe invocarse dentro de un .then(). Debe pasar done como un parámetro para obligar a mocha a esperar a la ejecución de done() para realizar la prueba. Si no, simplemente trata sus funciones it() como vacías porque el código dentro de ellas es asíncrono. Más adelante pego un ejemplo sobre pruebas mocha realizando cambios de estado de contratos.
OK gracias. Probé la sugerencia y obtuve un error de tiempo de espera (actualicé la pregunta en la segunda edición).
Mmm, creo que no deberías terminar tu código con then(/*code*/).done()... sino con then(/*código con la llamada*/).then(/*código realizando la prueba* /).then(hecho).catch(hecho).
Cargué un ejemplo en el que estaba trabajando: github.com/Sergeon/ethereum-truffle-solidity-ballot-contract Sin embargo, es truffle v.1, espero que ayude.