0
# Event Emission Testing
1
2
Matchers for verifying that transactions emit specific events with expected arguments, supporting complex event validation scenarios.
3
4
## Capabilities
5
6
### Event Emission Matcher
7
8
Tests if a transaction emits a specific event from a contract, with support for argument validation through chaining.
9
10
```typescript { .api }
11
/**
12
* Tests if transaction emits a specific event
13
* @param contract - Contract instance that should emit the event
14
* @param eventName - Name of the event to check for
15
* @returns EmitAssertion that can be chained with withArgs()
16
*/
17
emit(contract: any, eventName: string): EmitAssertion;
18
```
19
20
**Basic Usage Examples:**
21
22
```typescript
23
import { expect } from "chai";
24
import { ethers } from "hardhat";
25
26
// Test that event is emitted
27
await expect(contract.transfer(recipient, 1000))
28
.to.emit(contract, "Transfer");
29
30
// Test that event is NOT emitted
31
await expect(contract.noEventFunction())
32
.to.not.emit(contract, "Transfer");
33
34
// Test multiple events from the same transaction
35
await expect(contract.complexOperation())
36
.to.emit(contract, "OperationStarted")
37
.and.to.emit(contract, "OperationCompleted");
38
```
39
40
### Event Argument Validation
41
42
Chain `.withArgs()` to validate specific event arguments, supporting flexible matching patterns.
43
44
```typescript { .api }
45
interface EmitAssertion extends AsyncAssertion {
46
/**
47
* Validates specific arguments emitted with the event
48
* @param args - Expected argument values or predicates
49
* @returns AsyncAssertion for further chaining
50
*/
51
withArgs(...args: any[]): AsyncAssertion;
52
}
53
```
54
55
**Argument Validation Examples:**
56
57
```typescript
58
// Test event with exact argument values
59
await expect(contract.transfer(recipient, 1000))
60
.to.emit(contract, "Transfer")
61
.withArgs(owner.address, recipient.address, 1000);
62
63
// Test event with partial argument matching using anyValue
64
import { anyValue } from "@nomicfoundation/hardhat-chai-matchers/withArgs";
65
66
await expect(contract.mint(recipient, 500))
67
.to.emit(contract, "Mint")
68
.withArgs(recipient.address, anyValue); // Any amount
69
70
// Test event with unsigned integer validation
71
import { anyUint } from "@nomicfoundation/hardhat-chai-matchers/withArgs";
72
73
await expect(contract.updateBalance(user, 750))
74
.to.emit(contract, "BalanceUpdated")
75
.withArgs(user.address, anyUint); // Any positive integer
76
77
// Test event with complex argument combinations
78
await expect(contract.complexTransfer(from, to, amount, data))
79
.to.emit(contract, "ComplexTransfer")
80
.withArgs(from.address, to.address, amount, anyValue);
81
```
82
83
### Multiple Event Validation
84
85
Test multiple events from the same transaction or validate event sequences.
86
87
```typescript
88
// Multiple events from same transaction
89
await expect(contract.transferWithFee(recipient, 1000))
90
.to.emit(contract, "Transfer")
91
.withArgs(owner.address, recipient.address, 900)
92
.and.to.emit(contract, "Transfer")
93
.withArgs(owner.address, feeCollector.address, 100);
94
95
// Event sequences with different contracts
96
await expect(factory.createToken("MyToken", "MTK"))
97
.to.emit(factory, "TokenCreated")
98
.withArgs(anyValue, "MyToken", "MTK")
99
.and.to.emit(tokenTemplate, "Initialized")
100
.withArgs("MyToken");
101
```
102
103
### Event Testing with Addressable Objects
104
105
The matcher automatically resolves Addressable objects (contracts, signers) to their addresses for comparison.
106
107
```typescript
108
// Works with Addressable objects - automatically resolved to addresses
109
await expect(contract.transfer(recipient, 1000))
110
.to.emit(contract, "Transfer")
111
.withArgs(owner, recipient, 1000); // owner and recipient are resolved to addresses
112
113
// Equivalent to explicit address usage
114
await expect(contract.transfer(recipient.address, 1000))
115
.to.emit(contract, "Transfer")
116
.withArgs(owner.address, recipient.address, 1000);
117
```
118
119
### Advanced Event Testing Patterns
120
121
```typescript
122
// Test event not emitted with specific arguments
123
await expect(contract.partialTransfer(recipient, 100))
124
.to.emit(contract, "Transfer")
125
.withArgs(owner.address, recipient.address, 100)
126
.and.to.not.emit(contract, "TransferFailed");
127
128
// Test event emission order (multiple events of same type)
129
await expect(contract.batchTransfer([user1, user2], [100, 200]))
130
.to.emit(contract, "Transfer")
131
.withArgs(owner.address, user1.address, 100)
132
.and.to.emit(contract, "Transfer")
133
.withArgs(owner.address, user2.address, 200);
134
135
// Complex validation with custom predicates
136
const validateComplexEvent = (address: string, value: bigint, metadata: string) => {
137
return address.length === 42 && value > 0n && metadata.startsWith("0x");
138
};
139
140
await expect(contract.complexOperation())
141
.to.emit(contract, "ComplexEvent")
142
.withArgs(anyValue, anyUint, validateComplexEvent);
143
```
144
145
## Error Handling
146
147
The emit matcher provides detailed error messages for debugging:
148
149
```typescript
150
// If event is not emitted, error shows:
151
// "Expected event 'Transfer' to be emitted"
152
153
// If arguments don't match, error shows expected vs actual:
154
// "Expected event 'Transfer' with args [owner, recipient, 1000] but got [owner, recipient, 500]"
155
156
// If wrong contract emits the event:
157
// "Expected event 'Transfer' to be emitted by contract at 0x123... but was emitted by 0x456..."
158
```
159
160
## Chaining Limitations
161
162
The `.emit()` matcher supports chaining with other `.emit()` matchers but not with other async matchers:
163
164
```typescript
165
// ✅ This works - chaining multiple emit matchers
166
await expect(contract.operation())
167
.to.emit(contract, "EventA")
168
.and.to.emit(contract, "EventB");
169
170
// ❌ This doesn't work - chaining with balance change
171
await expect(contract.transfer(recipient, 1000))
172
.to.emit(contract, "Transfer")
173
.and.to.changeTokenBalance(token, recipient, 1000);
174
175
// ✅ Use separate assertions instead
176
const tx = contract.transfer(recipient, 1000);
177
await expect(tx).to.emit(contract, "Transfer");
178
await expect(tx).to.changeTokenBalance(token, recipient, 1000);
179
```
180
181
## Types
182
183
```typescript { .api }
184
interface EmitAssertion extends AsyncAssertion {
185
withArgs(...args: any[]): AsyncAssertion;
186
}
187
188
interface AsyncAssertion extends Assertion, Promise<void> {}
189
```