mock-socket icon indicating copy to clipboard operation
mock-socket copied to clipboard

How to spy on SocketIO.prototype.emit function in tests

Open Hughp135 opened this issue 6 years ago • 1 comments

I want to make an assertion that checks the 'emit' function was called on the client socket in my jasmine test.

I've tried spying on the client's instance of socket, but when the client connects to the mock server, it creates a new instance so the spy no longer applies.

Because the SocketIO class is not exported, I'm struggling to figure out how to spy on the prototype 'emit' function in the class SocketIO for my tests using Jasmine.

The obvious workaround would be to spy on the SocketIO instance after connecting, however in my code I am calling the 'emit' function inside the connect callback (on the client) so I can't do that :'(

Is my only option to make my assertions inside the mock server's on('message') callback?

Hughp135 avatar Apr 06 '18 10:04 Hughp135

I didn't try to spy the emit function but in the test I listen to the event emited and put a mock in the callback. Then when the emit is done I check if the mock has been called:

const openSocketServer = (eventsToListen) => new Promise((resolve) => {

    const mockServer = new Server(fakeURL, {
        forceNew: true,
        reconnection: false
    })

    mockServer.on('connection', socket => {
        socket.on(constants.WS_CONNECT, () => {
            clientConnected()
            resolve(mockServer)
        })

        socket.on(constants.WS_DISCONNECT, reason => {
            clientDisconnect(reason)
        })

        // I suppose this is what you are searching for
        if (eventsToListen) {
            eventsToListen.forEach(event => {
                socket.on(event[0], data => {
                    event[1](data)
                })
            });
        }
    })

    store.dispatch(openConnection('azertyuiop'))
})

This is a socket.io mock factory. I call it in each test and pass in argument an array containing arrays of 2 elements:

  • the name of the event emited by the client

  • the mock function that will be called. You can see that the mock is called with the argument of the event if I need to test it.

It return (resolve) the server so i can stop it at the end of the test.

Obviously you can directly listen to the event you emit without the genericity i've done with the double array. Like the socket.on(constants.WS_CONNECT, () => { } for example.

For exemple my tests look like:

it('- should emit join status room', async () => {
        const joinRooms = jest.fn()
        const mockServer = await openSocketServer([
            [constants.WS_JOIN, joinRooms]
        ])
        store.clearActions()

        store.dispatch({ type: MY_EVT_THAT_EMIT_SOC_EVT }) // it triggers some code in client that should emit the constants.WS_JOIN event

        expect(store.getActions()).toEqual([
            { type: MY_EVT_THAT_EMIT_SOC_EVT },
            { type: constants.SOME_OTHER_ACTION_DISPATCHED },
            { type: constants.WS_JOIN_ROOM, room: constants.WS_STATUS }
        ])
       // Here is what you are searching for
        expect(joinRooms).toHaveBeenCalledWith(constants.WS_STATUS)
        mockServer.stop()
})

Hope it will help

Chalisi avatar Jan 11 '19 09:01 Chalisi