使用松露直接测试以太坊事件
Testing ethereum events directly in solidity with truffle
我发现了以下问题,用于使用 javascript 测试 truffle 中的事件日志记录:
不过truffle也支持直接在solidity中编写测试。但是,我找不到任何关于如何测试事件日志记录的文档。有人可以帮我解决这个问题吗?
事件是存储在区块链中的日志。要获得事件,您需要观看链。
http://solidity.readthedocs.io/en/develop/contracts.html#events
Solidity Truffle 测试是合约。并且合同只是以太坊帐户存储代码。该代码在该帐户收到交易时执行。以太坊合约无法通过 watch chain 获取事件日志。所以Solidity不支持获取事件。
https://github.com/ethereum/wiki/wiki/White-Paper#ethereum-accounts
一般备注:
请注意,智能合约无法访问事件。事件是设计的,只能从智能合约外部访问。它们不直接存储在区块链中。这意味着您将无法使用纯可靠性进行测试。
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
使用 truffle 测试事件有效,只需按照以下步骤操作:
1) 创建发出事件的简单合约 (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) 创建测试合约(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) 创建测试代码(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) 运行 测试:
$ 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)
完整来源(package.json等):
https://github.com/MarkusSprunck/ethereum-event-scan
有关事件和监控的更多信息:
https://www.sw-engineering-candies.com/blog-1/Ethereum-Event-Explorer-for-Smart-Contracts
(免责声明:我是该项目和博客的作者)
考虑使用 OpenZeppelin 测试助手 expectEvent。
以构造期间发出的事件为例:
合同:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.3;
contract Test {
address public owner;
event ContractCreated();
constructor() {
owner = msg.sender;
emit ContractCreated();
}
}
松露测试:
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"
}
..
}
我发现了以下问题,用于使用 javascript 测试 truffle 中的事件日志记录:
不过truffle也支持直接在solidity中编写测试。但是,我找不到任何关于如何测试事件日志记录的文档。有人可以帮我解决这个问题吗?
事件是存储在区块链中的日志。要获得事件,您需要观看链。 http://solidity.readthedocs.io/en/develop/contracts.html#events
Solidity Truffle 测试是合约。并且合同只是以太坊帐户存储代码。该代码在该帐户收到交易时执行。以太坊合约无法通过 watch chain 获取事件日志。所以Solidity不支持获取事件。 https://github.com/ethereum/wiki/wiki/White-Paper#ethereum-accounts
一般备注:
请注意,智能合约无法访问事件。事件是设计的,只能从智能合约外部访问。它们不直接存储在区块链中。这意味着您将无法使用纯可靠性进行测试。
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
使用 truffle 测试事件有效,只需按照以下步骤操作:
1) 创建发出事件的简单合约 (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) 创建测试合约(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) 创建测试代码(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) 运行 测试:
$ 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)
完整来源(package.json等): https://github.com/MarkusSprunck/ethereum-event-scan
有关事件和监控的更多信息: https://www.sw-engineering-candies.com/blog-1/Ethereum-Event-Explorer-for-Smart-Contracts
(免责声明:我是该项目和博客的作者)
考虑使用 OpenZeppelin 测试助手 expectEvent。
以构造期间发出的事件为例:
合同:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.3;
contract Test {
address public owner;
event ContractCreated();
constructor() {
owner = msg.sender;
emit ContractCreated();
}
}
松露测试:
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"
}
..
}