Mock functions
Loading "Mock Functions"
Run locally for transcripts
We have an
Emitter
implementation that can add listeners to events and call those listeners whenever a matching event is emitted. This behavior is achieved by implementing two core methods on the emitter: .on()
and .emit()
:/**
* Add listener for the given event.
*
* @example
* const emitter = new Emitter<{ foo: [number] }>()
* emitter.on('foo', (data) => console.log(data))
*/
public on<Event extends keyof EventMap>(
event: Event,
listener: (...args: EventMap[Event]) => void,
): this {
const prevListeners = this.listeners.get(event) || []
const nextListeners = prevListeners.concat(listener)
this.listeners.set(event, nextListeners)
return this
}
/**
* Emit an event. This invokes all the listeners
* assigned for that event.
* @return {boolean} True if the emitted event has listeners.
*
* @example
* const emitter = new Emitter<{ foo: [number] }>()
* emitter.emit('foo', 123)
*/
public emit<Event extends keyof EventMap>(
event: Event,
...data: EventMap[Event]
): boolean {
const listeners = this.listeners.get(event) || []
for (const listener of listeners) {
listener.apply(this, data)
}
return listeners.length > 0
}
Here's how this emitter is used in our application:
// The owner of the emitter describes which events it can handle.
// This keeps the communication type-safe!
const emitter = new Emitter<{ login: [User] }>()
// The consumers then attach listeners to events...
emitter.on('login', (user) => {})
// ...and emit events when needed.
emitter.emit('login', currentUser)
We have a single intention behind the
Emitter
class:When event X is emitted, call all the listeners for that event.
One of the ways to write such a test is to make the event listener perform a side effect and assert on the result of that side effect.
const emitter = new Emitter<{ hello: [string]}>()
let isListenerCalled = false
emitter.on('hello', (firstName) => {
isListenerCalled = true
})
emitter.emit('hello', 'John')
expect(isListenerCalled).toBe(true)
If the value of theisListenerCalled
variable changes totrue
, we assume that thehello
event listener has been called.
It may seem like we are covering the intention here but in reality, we are not. All we are testing here is that
isListenerCalled
becomes true
at some point. The test will pass even if the initial value for the variable is set to true
by mistake, and fail if we forget to update it even if the listener has indeed been called correctly.In other words, in this test, we are testing what the listener does, not if it gets called. The fact of it getting called becomes an implicit assertion. Such assertions are tremendously useful but the main intention we are testing must always be clear and explicit.
π¨βπΌ Your task in this exercise is to write a proper test for the listener being called, using the
vi.fn()
utility from Vitest. Verify your solution by running npm test
.