Why should I assert Events in my Cypress tests?
When performing a Cypress test you are using an application as a real user would use it. From this point of view, we could assume that events are not important for cypress tests since a user also will not see them.
Maybe testing events is more a thing for Unit or Component tests?
I think they are not! I think it is important to also assert on events. Events are a way to transfer data between components. If the transfers data is wrong the behavior of the component would be wrong too, even if the UI may still look correct. So in my opinion it is crucial to test events within cypress tests.
I initially thought this is a trivial task, but I got stuck at it for nearly a day.
I tried to access the event I want to listen to and work my way further with invokes and spies. I tried to add the spy in the before each. I tried to listen to the events using Cypress jquery methods. But I had every time the problem that my listeners inside cypress seemed to be ignored. Or the test seemed to assert or end before the listener was listening. So how did I solve this?
How to assert events in Cypress
Unfortunately, Cypress and events are not a good team. It took me days to figure out how to test custom events emitted from components in my Cypress tests. But I finally got it after working through every resource I found on google and doing hours of trial and error (many errors) testing I made my way through a guide originally made for React and redux store event testing in cypress.
To assert events in Cypress you first need to set up the kuker event library. Then you can simply access the Kuker instance from cypress and access all the custom events from your components inside your cypress tests.
Let's prepare an example and go through this step by step.
step by step guide to assert events in cypress
To try this out I have prepared a simple example inside my Testing Playground.
It seems like kuker is not working in my production environment. So feel free to copy the below code and play around with cypress and kuker.
Before we start, the test components:
playground.vue
<template>
<Layout>
<div class="container">
<extroverted-div data-cy="component" @customEvent="handleCustomEvent"/> {{ divResult}}
</div>
</Layout>
</template>
<script>
import ExtrovertedDiv from "../components/codingExamples/extrovertedDiv";
export default {
components: {
ExtrovertedDiv,
},
data () {
return{
divResult : ""
}
},
methods : {
handleCustomEvent(payload){
console.log("handleCustomEvent")
this.divResult = payload;
}
},
}
</script>
ExtrovertedDiv.vue
<template>
<div @click="emitEvent" id="why" data-cy="whyCypress">
Why should i use Cypress?
</div>
</template>
<script>
import { VueEmitter } from 'kuker-emitters';
VueEmitter();
export default {
name: "extrovertedDiv",
methods: {
emitEvent(){
this.$emit("customEvent", "Because it is amazing!");
}
}
}
</script>
<style scoped>
</style>
-
Install Kuker
What is kuker?
Kuker is a package and browser extension to access and visualize all events fired from your application. Kuker was developed for react, but works with all major frameworks like angular and vue.
Take a look at Kuker
to install kuker in your application run
npm install kuker-emitters
to install the kuker extension and have everything visible visit the chrome store.
-
Activate Kuker inside the app
To activate kuker and make use of its emitters paste the following snippet on top of your vue components script section. As I did in exportedDiv.vue
import { VueEmitter } from 'kuker-emitters';
VueEmitter();
-
Save Kuker events inside Cypress
To save kuker events inside cypress we need to access the kuker instance before each test. After accessing the instance I will save the result in two arrays. One Contains all events fired and one contains also the interesting events with a payload.
let kukerEvents
let vueEvents
beforeEach(() => {
kukerEvents = []
vueEvents = []
cy.visit('https://the-koi.com/playground', {
onBeforeLoad (win) {
const postMessage = win.postMessage.bind(win)
win.postMessage = (what, target) => {
// check if the event is a kuker event
if (isKuker(what)) {
let raw = Cypress._.pick(what, 'state');
let obj = null
try{
// save events with a payload here
obj = JSON.parse(raw.state);
vueEvents.push(obj)
}catch(e){
console.log(e);
}
// save all the events here
// you would not need them,
// this is just interesting to get a deeper insight
// into vue
kukerEvents.push(Cypress._.pick(what, 'label', 'state'))
}
return postMessage(what, target)
}
// save for sync
cy.spy(win, 'postMessage').as('postMessage')
}
})
});
-
assert with kuker events
inside the test we then can now access all the events and assert on them.
I do this after performing the click three times, but I suggest you play also around with the alias and see how kuker behaves for different actions. This is then a great example to understand how kuker works and how to use kuker to access custom events inside cypress
it.only("asserts event with kuker", () =>{
cy.get('[data-cy="component"]').click().click().click().then(()=>{
console.log("kuker store: ", kukerEvents)
console.log("vue event store: ", vueEvents)
// we expect to get 3 messages from Vue
expect(vueEvents.length).to.be.greaterThan(3);
// the event should contain the sent message
expect(vueEvents.find(e =>
e.eventName === "customEvent").
payload).to.includes("Because it is amazing!");
})
// directly listen to post message
cy.get("@postMessage").then(msg => {
console.log("post msg: ", msg)
console.log("kuker store: ", kukerEvents)
console.log("vue event store: ", vueEvents)
})
})
-
feel great
You now can write stronger tests by also asserting on custom events from your components! Congratulations!
Here is the complete test file for better understanding:
/// <reference types="cypress" />
const isKuker = what =>
Cypress._.isPlainObject(what) && what.kuker && what.type !== 'NEW_EMITTER'
const kukerMessage = ke => (ke.label ? `${ke.type}: ${ke.label}` : ke.type)
context('customEvent Assertions', () => {
let kukerEvents
let vueEvents
beforeEach(() => {
kukerEvents = []
vueEvents = []
cy.visit('https://the-koi/playground', {
onBeforeLoad (win) {
const postMessage = win.postMessage.bind(win)
win.postMessage = (what, target) => {
if (isKuker(what)) {
let raw = Cypress._.pick(what, 'state');
let obj = null
try{
obj = JSON.parse(raw.state);
vueEvents.push(obj)
}catch(e){
console.log(e);
}
kukerEvents.push(Cypress._.pick(what, 'label', 'state'))
}
return postMessage(what, target)
}
cy.spy(win, 'postMessage').as('postMessage')
}
})
});
describe('find and assert on custom events', () => {
it.only("asserts event with kuker", () =>{
cy.get('[data-cy="component"]').click().click().click().then(()=>{
console.log("kuker store: ", kukerEvents)
console.log("vue event store: ", vueEvents)
// we expect to get 3 messages from Vue
expect(vueEvents.length).to.be.greaterThan(3);
// the event should contain the sent message
expect(vueEvents.find(e => e.eventName === "customEvent").
payload).to.includes("Because it is amazing!");
})
})
});
});
Conclusion
Accessing custom events within cypress tests could be hard, but with the right tools in place it is easy. You will end up with stronger cypress tests, a nice additional debug tool, and with a better understanding of your application's event flow.
I hope I could provide you with some value with this article. If you want to support me and the work I´m doing you can buy me a coffee. I will donate half of it to Ukraine.
Happy, strong asserting,
Alex