Pruebas unitarias escritas en Solidity, cómo cambiar la dirección de msg.sender, msg.value,

Me gustaría saber si es posible cambiar la dirección del remitente del mensaje de llamada de función del contrato al escribir pruebas unitarias en solidez. Realmente debería serlo, estaba tratando de averiguar cómo pero no pude.

pragma solidity ^0.4.4;

import "truffle/Assert.sol";
import "truffle/DeployedAddresses.sol";

contract A {

    event Test(address add);

    function test() {
        Test(msg.sender);
    }
}

contract TestA {

    function test01() {
        A a = A(DeployedAddresses.A());
        a.test(); // <--- msg.sender is address(this), but how do we use a different account from testrpc?
    }
}

En gitter obtuve una respuesta, así que intenté ejecutarlo así:

a.test({from: 0xf6a948bff792e4f42d7f17e5e4ebe20871d160f2});

Da el siguiente error:

TypeError: Wrong argument count for function call: 1 arguments given but expected 0. a.test({from: 0xf6a948bff792e4f42d7f17e5e4ebe20871d160f2});

Sistema operativo: Windows 10 Truffle versión: v3.4.11 Cliente Ethereum: testrpc v4.1.3 versión de nodo: v8.7.0 npm versión: 5.4.2

Respuestas (3)

No puede cambiar la dirección del remitente desde Solidity. Las pruebas de solidez se ejecutan dentro de la EVM y no permite modificar el msg.sender. Es posible que una versión modificada de EVM proporcione dicha funcionalidad, pero no estoy al tanto de dicha modificación.

Puede modificar la función para probar para aceptar un parámetro adicional y pasar allí el remitente que desee

contract A {
    // We cannot test directly deposit
    function deposit(uint _amount) public {
        doDeposit(msg.sender, _amount);
    }
    // But we can test doDeposit
    function doDeposit(address _sender) public {
    }
}

contract TestA {
    address constant senderA = "0xA00...";
    address constant senderB = "0xB00...";
    function testDeposit() {
        A a = A(DeployedAddresses.A());
        a.doDeposit(senderA, 10000); 
        a.doDeposit(senderB, 10000); 
    }
}

No es ideal, pero puede ayudar a encontrar algunos errores.

Otra opción es usar la dirección del contrato de prueba

contract TestA {
    address constant senderA = "0xA00...";
    address constant senderB = "0xB00...";
    function testMint() {
        // TestA is the owner a
        A a = new A(this);
        // Only the owner TestA can mint
        a.mint(senderA, 10000);
        // make senderA approve TestA
        a.doApprove(senderA, this, 1000);
        // testA can make a transfer from senderA
        a.transferFrom(senderA, senderB, 1000);
    }
}
Buena idea. ¿Algún comentario sobre encapsular esto para que no se pueda alcanzar fuera del alcance del contrato de prueba? Implementarlo de esa manera generaría muchas vulnerabilidades o algunos códigos y códigos de prueba deberían comentarse antes de la compilación.
He visto que algunos contratos tienen un onlyTestmodificador que verifica una testvariable, pero es muy feo.
¿Alguien puede mostrar una forma de lograr lo mismo mientras usa remix_tests dentro de Remix IDE?
@momoja Es mejor crear una pregunta separada para eso. Nadie se dará cuenta del comentario.
@Ismael gracias, publiqué aquí ethereum.stackexchange.com/questions/127101/…

Dado que tiene un contrato (TestA) que llama a una función de otro contrato (A), msg.sender siempre será la dirección de TestA en la función de prueba de A, porque, para el contrato A, la dirección que envió el mensaje fue TestA.

Si desea registrar la dirección real que llamó a la función originalmente, tendría que hacer lo siguiente:

pragma solidity ^0.4.4;

import "truffle/Assert.sol";
import "truffle/DeployedAddresses.sol";

contract A {

    event Test(address add);

    function test(address _sender) {
        Test(_sender);
    }
}

contract TestA {

    function test01() {
        A a = A(DeployedAddresses.A());
        a.test(msg.sender); 
    }
}
Es una buena idea. Por favor vea mi comentario a Ismael. Además, esto no funcionaría para la mayoría de los modificadores, ¿verdad? Estoy pensando que también podrían cambiarse de esta manera.

Aquí hay un enfoque alternativo que he estado usando de manera efectiva para manipular msg.senderpruebas unitarias escritas en solidez:

  1. Crear contratos de "actor", es decir, contratos que implementen un comportamiento de actor de prueba unitaria y que realicen llamadas al contrato bajo prueba (CUT)
  2. Implemente un contrato de actor para cada dirección individual que necesite para escribir sus pruebas unitarias. Por ejemplo, si está escribiendo casos de prueba para un contrato ERC20, puede implementar a SenderContracty a ReceiverContract, cada uno tendrá su propia dirección y cuando llamen, las funciones CUT msg.senderse establecerán en sus direcciones implementadas.
  3. Ahora, al escribir pruebas unitarias en lugar de llamar a las funciones CUT, componga directamente el caso de prueba como una secuencia de llamadas a la función del actor ( msg.sendercontendrá la dirección del contrato del actor en estas llamadas)