简体   繁体   中英

Testing ethereum events directly in solidity with truffle

I found the following question for testing event logging in truffle using javascript:

Test ethereum Event Logs with truffle

But truffle also supports writing tests directly in solidity. However, I can't find any documentation for how to test event logging in solidity. Can somebody help me with this?

Events are logs stored at blockchain. To get an event you need to watch the chain. http://solidity.readthedocs.io/en/develop/contracts.html#events

Solidity Truffle tests are contracts. And contracts just Ethereum accounts storing code. That code gets executed when that account receives a transaction. Ethereum contracts can not watch chain to get an event logs. So Solidity does not support getting events. https://github.com/ethereum/wiki/wiki/White-Paper#ethereum-accounts

General remarks:

Be aware that a smart contract can't access events. Events are by design just accessible from outside a smart contract. They are not directly stored in the blockchain. This means that you will not be able to test with pure solidity.

The Log and its event data is not accessible from within contracts (not even from the contract that created them). Source:https://solidity.readthedocs.io/en/v0.5.3/contracts.html#events

Testing events with truffle works, just follow these steps:

1) Create simple contract that emits events ( contracts/EventEmitter.sol ):

    pragma solidity 0.5.12;

    contract EventEmitter {

    // ---- EVENTS -----------------------------------------------------------------------------------------------------
    event ConstructorDone(address owner, string message);
    event Counter(uint64 count);

    // ---- FIELDS -----------------------------------------------------------------------------------------------------
    uint64 private _count = 0;
    string constant _message = '0x0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789';

    // ---- CONSTRUCTOR ------------------------------------------------------------------------------------------------
    constructor() public {
        emit ConstructorDone(msg.sender, _message);
    }

    // ---- STATISTICS FUNCTIONS ---------------------------------------------------------------------------------------
    function getCount() public view returns (uint count) {
        return _count;
    }

    // ---- CORE FUNCTIONS ---------------------------------------------------------------------------------------------
    function increment() public {
        _count++;
        emit Counter(_count);
    }
}

2) Create test contract ( test/TestAnEventEmitter.sol ):

pragma solidity 0.5.12;

import "truffle/Assert.sol";
import "../contracts/EventEmitter.sol";

contract TestAnEventEmitter {

    EventEmitter private eventEmitter;

    uint eContracts = 0;

    address private owner;

    function assertCount() private {
        Assert.equal(eventEmitter.getCount(), eContracts, "Unexpected count of created contracts");
    }

    constructor() public{
        eventEmitter = new EventEmitter();
        owner = address(this);
    }

}

3) Create Test Code ( test/TestAnEventEmitter.js ):

const EventEmitter = artifacts.require("EventEmitter");
const truffleAssert = require('truffle-assertions');

/** Expected number of counter */
var eCount = 0;

/** The Contract's instance */
var eventEmitter;

global.CONTRACT_ADDRESS = '';

async function assertContractCount() {
    assert.equal(await eventEmitter.getCount.call(), eCount, "Wrong number of created contracts");
}

contract('EventEmitter', async () => {

    before(async () => {
        eventEmitter = await EventEmitter.new();
    });

    describe("1.1 Basic", function () {

        it("1.1.1 has been created", async () => {
            global.CONTRACT_ADDRESS = eventEmitter.address;
            console.log('        contract => ' + global.CONTRACT_ADDRESS);
            await assertContractCount();
        });

        it("1.1.2 should emit ConstructorDone event", async () => {
            // Get the hash of the deployment transaction
            let txHash = eventEmitter.transactionHash;

            // Get the transaction result using truffleAssert
            let result = await truffleAssert.createTransactionResult(eventEmitter, txHash);

            // Check event
            truffleAssert.eventEmitted(result, 'ConstructorDone', (ev) => {
                console.log('        owner => ' + ev.owner);
                return true;
            });
        });
    });

    describe("1.2 Check calls of increment()", function () {

        it("1.2.1 first call should increase the counts correctly", async () => {
            // Pre-Conditions
            await assertContractCount();

            // Creation
            let tx = await eventEmitter.increment();
            eCount++;

            // Expected Event
            truffleAssert.eventEmitted(tx, 'Counter', (ev) => {
                return parseInt(ev.count) === eCount;
            });

            // Post-Conditions
            await assertContractCount();
        });

        it("1.2.2 second call should increase the counts correctly", async () => {
            // Pre-Conditions
            await assertContractCount();

            // Creation
            let tx = await eventEmitter.increment();
            eCount++;

            // Expected Event
            truffleAssert.eventEmitted(tx, 'Counter', (ev) => {
                return parseInt(ev.count) === eCount;
            });

            // Post-Conditions
            await assertContractCount();
        });

        it("1.2.3 third call should increase the counts correctly", async () => {
            // Pre-Conditions
            await assertContractCount();

            // Creation
            let tx = await eventEmitter.increment();
            eCount++;

            // Expected Event
            truffleAssert.eventEmitted(tx, 'Counter', (ev) => {
                return parseInt(ev.count) === eCount;
            });

            // Post-Conditions
            await assertContractCount();
        });
    });
});

4) Run Tests:

$ truffle test
Using network 'development'.


Compiling your contracts...
===========================
> Compiling ./test/TestAnEventEmitter.sol



  Contract: EventEmitter
    1.1 Basic
        contract => 0xeD62E72c2d04Aa385ec764c743219a93ae49e796
      ✓ 1.1.1 has been created (56ms)
        owner => 0xbD004d9048C9b9e5C4B5109c68dd569A65c47CF9
      ✓ 1.1.2 should emit ConstructorDone event (63ms)
    1.2 Check calls of increment()
      ✓ 1.2.1 first call should increase the counts correctly (142ms)
      ✓ 1.2.2 second call should increase the counts correctly (160ms)
      ✓ 1.2.3 third call should increase the counts correctly (156ms)

Full sources (with package.json, etc.): https://github.com/MarkusSprunck/ethereum-event-scan

More about events and monitoring: https://www.sw-engineering-candies.com/blog-1/Ethereum-Event-Explorer-for-Smart-Contracts

(Disclaimer: I'm the author of this project and blog)

Consider using OpenZeppelin Test HelperexpectEvent .

Take for example event emitted during the construction:

Contract :

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.3;

contract Test {
    address public owner;

    event ContractCreated();

    constructor() {
        owner = msg.sender;

        emit ContractCreated();
    }
}

truffle-test :

const { expectEvent } = require('@openzeppelin/test-helpers');

const TestContract = artifacts.require('Test');

contract('Test', function (accounts) {
    const [owner] = accounts;
    const txParams = { from: owner };

    beforeEach(async function () {
        this.testContract = await TestContract.new(txParams);
    });

    describe('construction', function () {
        it('initial state', async function () {
            expect(await this.testContract.owner()).to.equal(owner);

            await expectEvent.inConstruction(this.testContract, 'ContractCreated');
        });
    });
});

package.json

{
..
  "devDependencies": {
    "@openzeppelin/test-helpers": "^0.5.10"
  }
..
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM