I want to test my js app that uses phoenix.js sockets. I want to write tests along the lines of:
- Set up a mocked socket and channel.
- Render my app.
- Interact with the app.
- Assert that a message was sent on the channel.
- Mock a response to that message as if the server had replied.
- Assert that the UI was updated with the response.
Is there a recommended way to do this?
I’m familiar with using jest to mock functions, but I’m struggling to set it up through the layers of callbacks and classes in a way that lets me assert something was pushed or send a reply.
Sorry, I’m a bit late to the party here.
Using vitest, I was able to accomplish creating a phoenix channel mock doing something like this:
import { vi } from 'vitest';
import * as phoenix from 'phoenix';
vi.mock('phoenix', () => {
return {
Socket: vi.fn().mockImplementation(() => fakeSocket)
};
});
const fakeChannel = {
join: vi.fn().mockReturnThis(),
push: vi.fn().mockReturnThis(),
on: vi.fn().mockReturnThis(),
off: vi.fn().mockReturnThis(),
leave: vi.fn().mockReturnThis(),
receive: vi.fn().mockImplementation((event, callback) => {
if (event === 'ok') {
callback({});
}
return this;
})
};
const fakeSocket = {
connect: vi.fn(),
channel: vi.fn(() => fakeChannel),
// mock any other used Socket class methods here
};
Then inside of your tests, you can override the mocks as needed:
describe("Example", () => {
it("can join a channel", async () => {
await joinChannel(); // This is the `socket.channel(...).join().receive("ok", callback)` wrapped by a promise that resolves in the callback
expect(fakeSocket.connect).toHaveBeenCalledOnce();
expect(fakeChannel.join).toHaveBeenCalledOnce();
});
it("can receive pushed event", async () => {
await joinChannel();
fakeChannel.on.mockImplementation((event, handler) => {
if (event == "pushed:event") handler("Some data");
return fakeChannel;
});
const result = await listenForPushedEvent(); // This is the function that sets up `channel.on("pushed:event", callback)` wrapped in a promise that resolves in the callback
expect(result).toEqual("Some data");
});
});
The challenge still exists that you have to carefully craft your mocks, which can be difficult since the Phoenix JS API is callback based.