loading...

How to test your react websocket implementation using cypress

full_stack_adi profile image Aditya Originally published at adityanaik.dev on ・2 min read

Set up demo application

I scaffold a standard react application

$ npx create-react-app ws-demo

Clean up and update app.js to set up a websocket client.

import React, { useState } from 'react'

const App = () => {
  const [message, setMessage] = useState('websocket is closed')

  return (
    <div className="App">
      <p id="websocket">{message}</p>
      <WebsocketHandler setMessage={setMessage} />
    </div>
  )
}

export default App

// WebsocketHandler does what the name suggests - launch/close websocket client and receive messages

const websocketUrl = 'ws://127.0.0.1:5000'
const WebsocketHandler = ({ setMessage }) => {
  const ws = new WebSocket(websocketUrl)
  ws.onopen = () => {
    console.log('conn open')
    ws.send('connection is open')
  }
  ws.onmessage = (message) => {
    setMessage(message.data)
    console.log('onmessage triggered', message)
    ws.send('message received')
  }
  ws.onclose = () => {
    console.log('connection closed')
  }
  return null
}

As you can see, I have a WebsocketHandler component which handles websocket and sets state in app.js when it receives the message.

Set up and scaffold Cypress

Cypress is a fantastic testing framework.
It is easy to set up and can be picked up pretty quickly.

Setting up Cypress is pretty straight forward - just run:

$ npm install cypress
or
$ yarn add cypress

I will also install the recommended dependency

$ npm install -D start-server-and-test

start-server-and-test is a cool tool that basically

Starts server, waits for URL, then runs test command; when the tests end, shuts down server

as explained on their github repo.

I install that package and use it in the package.json script -

"cy:test": "start-server-and-test start http://localhost:3001 cy:open"

Install the manual-web-socket package

I am going to install a websocket testing package manual-web-socket
(github) and use it.

Writing the test

My Cypress test will follow the following steps-

  • Require manual-web-socket package
  • Use the nifty onBeforeLoad to access websocket services and attach them to our beloved win object in cypress
  • Finally, set up a mock connection
    • Change connection status to OPEN
    • send a message, and assert that it shows up in our react app

The test itself (with comments) -

/// <reference types="Cypress" />

const manualWebSocket = require('manual-web-socket') // import the package

describe('Tests websocket', () => {
  it('Successfully processes websocket message from server', () => {
    cy.visit('/')
      .get('[id=websocket]')
      .should('have.text', 'websocket is closed')

    cy.visit('/', {
      onBeforeLoad(win) {
        var script = win.document.createElement('script')
        script.innerText = manualWebSocket.getScript()
        win.document.head.appendChild(script)
        win.mws.track(['ws://127.0.0.1:5000']) // we start tracking ws connection here
      },
    }).then((win) => {
      const mws = win.mws
      const trackedConnection = mws.trackedConnections.getByUrl(
        // get connection by URL
        'ws://127.0.0.1:5000'
      )
      trackedConnection.readyState = mws.readyState.OPEN // set the ws state to OPEN
      const connOpenMessage = 'connection open with client'
      const payload = { data: 'Cypress is connected via websocket' }
      trackedConnection.addServerScenario(
        // addServerScenario to mock ws server on the other side
        'connection open with client',
        (connection, message) => {
          connection.reciveMessage(payload)
        }
      )
      trackedConnection.send(connOpenMessage) // send message to ws client
      cy.get('[id=websocket]').should(
        'have.text',
        'Cypress is connected via websocket' // Assert the change in client state
      )
      trackedConnection.readyState = mws.readyState.CLOSED // close ws connection
    })
  })
})

The test goes green.

Posted on by:

full_stack_adi profile

Aditya

@full_stack_adi

Full Stack Developer #React #Rails #Express

Discussion

markdown guide