What's the best way to check that events were fired in a Truffle test?
solidity – How to Check Events in Truffle Tests
eventssoliditytestingtruffle
Related Solutions
Seeing as there isn't a detailed updated answer:
As @pors mentioned, web3.js has a getPastEvents
function. You can have it run at startup, using a syntax like:
myContract.getPastEvents('MyEvent');
The docs for this function are here. You can also filter by a specific topic, set a range of blocks to check, and more. Here's an expanded example, taken straight from the docs linked above:
myContract.getPastEvents('MyEvent', {
filter: {myIndexedParam: [20,23], myOtherIndexedParam: '0x123456789...'}, // Using an array means OR: e.g. 20 or 23
fromBlock: 0,
toBlock: 'latest'
}, function(error, events){ console.log(events); })
.then(function(events){
console.log(events) // same results as the optional callback above
});
This will not continue listening for events to the best of my knowledge. You could do this with events
, however:
MyContract.events.MyEvent()
The function takes an object with parameters as an argument, much like getPastEvents()
, see the docs here for more details. Based on the question, this would seem to be the best fit for the OP's particular use case.
(There is a similar function called allEvents
for subscribing to all events from a particular contract - docs)
@pors also suggests using subscribe
to get past events, and to continue to listen for new events. The docs are here. Note that you'll need to provide the topics
you want to listen for. (Here's an explainer for event topics, you can get the topic for your event by hashing the event signature (eg Transfer(address,address,uint256)
of the event with keccak256).
I've discovered that not all events are displayed in the truffle output window, although they might have fired correctly with the execution of a contract. I believe this to still be an issue
After spending hours on this today I have come up with a solution to test that specific events are fired.
Here's my example truffle javascript test:
it("should do something that fires SaleMade", function() {
return myContract
.stockShelf("beer", "wine", {from: sellerAccount})
.then(() => myContract.sell("water", "bread", {from: sellerAccount}))
.then(() => utils.assertEvent(myContract, { event: "SaleMade", logIndex: 1, args: { name: "bread" }}));
}
The above filters the fired events that match the filter object passed to the assertEvent utility function I have in utils.js in the same folder. At the top of my javascript test I have declared:
var utils = require("./utils.js");
A snippet of my utils.js class is as follows:
var _ = require("lodash");
var Promise = require("bluebird");
module.exports = {
assertEvent: function(contract, filter) {
return new Promise((resolve, reject) => {
var event = contract[filter.event]();
event.watch();
event.get((error, logs) => {
var log = _.filter(logs, filter);
if (log) {
resolve(log);
} else {
throw Error("Failed to find filtered event for " + filter.event);
}
});
event.stopWatching();
});
}
}
This requires other npm packages that aren't included with truffle by default. By default truffle doesn't include npm packages. I setup npm and installed the required packages like this:
npm init
npm install bluebird --save
npm install lodash --save
EDIT: Using testrpc
Best Answer
I wrote the
truffle-assertions
package just for this. It has an assertion to check that an event has been emitted. Essentially, it is similar to the older answer here, but it does not need to check a specific index of the logs, and it has the option add complex conditions in a straightforward way by passing a filter function.You can import it at the top of your test file:
And use it inside your test:
I wrote a blog post that goes over the functionality with a detailed use case for further reading: Checking events when testing Solidity smart contracts with Truffle