-
Notifications
You must be signed in to change notification settings - Fork 3.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Mock websockets #2492
Comments
Related: sinonjs/sinon#174 |
Hi Any news about this feature ? Thanks |
@EmmanuelDemey you can already mock WebSockets in Cypress (as a workaround) the same way you do it for fetch, using onBeforeLoad(win) {
// Call some code to initialize the fake server part using MockSocket
cy.stub(win, "WebSocket", url => new MockSocket.WebSocket(url))
} |
How did you solve the issue with |
Which issue? You just need the |
I would be interested in seeing a full example of your implementation of the MockSocket Library. I'm struggling to get it to work correctly. (partly because I was trying to use the RxJS WS Subject) In your example, you are passing the URL from the window object it looks like is that right? |
Yes. a full example should be very helpful. |
I have an example that is almost working the way I want it to now. Having an issue with the Server I believe. for some reason, it will not close and tries to reconnect and cypress keeps picking up xhr requests so it doesn't close. However, I was able to stub out the client web socket correctly. cy.visit('/', {
onBeforeLoad: (win) => {
cy.stub(win, 'WebSocket', (url) => {
MockServer = new Server(url)
MockServer.on('connection', (socket) => {
socket.send(alert)
setTimeout(() => {
socket.close()
MockServer.close()
}, 1000)
})
return new WebSocket(url) /* mock socket */
})
}
}) |
Here is a more complete example. I've been using this for a year to test applications without any issue. In a const sockets = {}
export function initServer() {
// useful to reset sockets when doing TDD and webpack refreshes the app
for (const socket of Object.values(sockets)) {
socket.close()
}
mockServer()
}
function mockServer() {
// Of course, your frontend will have to connecto to localhost:4000, otherwise change this
sockets.mockServer = new Server("ws://localhost:4000")
sockets.mockServer.on("connection", socket => {
sockets.server = socket
// Will be sent any time a client connects
socket.send("Hello, world!")
socket.on("message", data => {
// Do whatever you want with the message, you can use socket.send to send a response
}
}
} You can also use setInterval to send events regularly, and even export some functions to trigger an event from the server in a test. You then just have to stub websocket in the visit Cypress method: import { initServer } from "./server.js"
cy.visit("/", {
onBeforeLoad(win) {
initServer()
cy.stub(win, "WebSocket", url => new MockSocket.WebSocket(url))
}
}) That's all. |
@theAndrewCline thanks for that example, I've got a test working but it only works for one test, then it says that the mock server is already listening on that url: describe('mock socket method 1', () => {
let mockSocket;
let mockServer;
beforeEach(() => {
cy.visit('/', {
onBeforeLoad(win: Window): void {
// @ts-ignore
cy.stub(win, 'WebSocket', url => {
mockServer = new Server(url).on('connection', socket => {
console.log('mock socket connected');
mockSocket = socket;
});
if (!mockServer) return new WebSocket(url);
});
},
});
});
afterEach(() => {
mockSocket.close()
});
it('gets a message', () => {
const object = _createSettingsApiPutPayload(defaultSettingsState)
mockSocket.send(JSON.stringify(object));
cy.contains('Motion threshold')
});
it('gets a message', () => {
const object = _createSettingsApiPutPayload(defaultSettingsState)
mockSocket.send(JSON.stringify(object));
cy.contains('Motion threshold')
});
}); If I change the method to |
@tuzmusic you're only closing the socket but not stopping the server, have you tried to call |
do we have to use cy.visit('/', {...}) and not specific url inside my app? why? |
We don't: you can set the So Without the |
@tuzmusic I am trying to use the same code snippet, in my code, but I always get this error |
Just my two cents, this custom cypress command for graphQl websocket mocks works for me: import { Server, WebSocket } from "mock-socket"
const mockGraphQlSocket = new Server(Cypress.env("GRAPHQL_WEB_SOCKET"))
Cypress.Commands.add("mockGraphQLSocket", mocks => {
cy.on("window:before:load", win => {
win.WebSocket = WebSocket
mockGraphQlSocket.on("connection", socket => {
socket.on("message", data => {
const { id, payload } = JSON.parse(data)
if (payload && mocks.hasOwnProperty(payload.operationName)) {
mocks[payload.operationName](
// Delegate the socket send call to the party owning the mock data since multiple calls might need to be made (for example to update multiple individual entries)
data =>
socket.send(
JSON.stringify({
type: "data",
id,
payload: { errors: [], data }
})
),
payload.variables
)
}
})
})
})
}) And then the command could be used something like this (depending on the query requirements) cy.mockGraphQLSocket({
yourGraphQlQuery: (send, { uuids }) => {
uuids.forEach(id => {
send({
yourGraphQlQuery: {
...yourMockData,
id
}
})
})
}
}) |
@BobD looks nice. How do you close you mock server though? |
@theAndrewCline Good question, haven't though about that. I guess the mock-socket |
I gave this another go today based on few comments above, using |
Before instantiating a new
|
Does anyone work with AWS (AppSync) implementation of WebSockets? I can't connect mock, every time I'm getting an error: WebSocket connection to {url} failed |
Anyone had any luck with mocking socket.io? |
@erezcohen I managed to mock socket.io using socktet.io-mock and custom commands. Test cy.mockSocketIO();
cy.visit("/");
cy.pushSocketIOMessage("chat", "Hello World"); Commands import SocketMock from "socket.io-mock";
let socket = new SocketMock();
Cypress.Commands.add("mockSocketIO", (mocks) => {
cy.on("window:before:load", (window) => {
window.io = socket;
});
});
Cypress.Commands.add("pushSocketIOMessage", (event, payload) => {
socket.socketClient.emit(event, payload);
setTimeout(() => {}, 1000);
}); Application if (window.io) {
this.socket = window.io;
} else {
this.socket = io("/");
} I am not happy with the application part. But could not find another way to mock the socket.io-client. |
@quentinus95 : Where is new Server() being defined in this example? Is the a specific dependency you need? |
@edjumacator you can import it from the |
I used the "socket.io-mock" when testing socket.io chat, here is a video https://www.youtube.com/watch?v=soNyOqpi_gQ |
@bahmutov do you happen to know of any examples for mocking GraphQL Subscriptions? |
Unaware of any examples. It would be interesting if you can make a simple demo app in a GitHub repo
…Sent from my iPhone
On Jul 5, 2021, at 14:57, mrpicklez70 ***@***.***> wrote:
@bahmutov do you happen to know of any examples for mocking GraphQL Subscriptions?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or unsubscribe.
|
I tried to use the code shown in this comment, but, it did not work for me. That's probably because Cypress queues it's commands. So, I now have combined the code examples of these 2 comments and modified them according to the existing Cypress capabilities: Here's the code that worked for me // server.js
import { Server } from 'mock-socket'
export const getServer = () => {
return new Cypress.Promise(resolve => {
// Initialize server
const mockServer = Server('wss://some-url-for-socket/')
let mockSocket
mockServer.on('connection', (socketHandle) => {
resolve(socketHandle)
})
})
} // Our e2e test file
import { getServer } from `server.js`
import { WebSocket } from 'mock-socket'
it('should send mock socket message', () => {
// Create mock server
const socketPromise = getServer()
// visit the page to establish connection
cy.visit('/some-page-that-is-to-be-tested', {
onBeforeLoad: (win) => {
// Stub out JS WebSocket
cy.stub(win, 'WebSocket', url => new WebSocket(url))
}
})
// Resolve the Promise to get the server's socket handle
// This is done after `visit` because that's when a connection would have been established
cy.wrap(socketPromise).then((mockSocket) => {
// Use the `mockSocket` variable to send a message to client
mockSocket.send('Some message to the client')
// After this, write code that asserts some change that happened due to the socket message
})
}) Explanation for the code:
|
Hi, following this thread and wanted to throw in my recent finding in case it might help anyone... In a demo session with a few colleagues, we played around with web sockets and came up with a way to test messages sent on connection and incoming messages based on @kanteankit solution. #2492 (comment) Demo code is available here: https://github.com/AgileVentures/wtw_ws_chat_demo |
I've used the implementation from @tochman. However I ran into erros using the mock-socket library. I am using the fake-socket library. It is working without any problems now. |
@quentinus95 In this what is MockSocket and where it is imported from? |
@theAndrewCline |
Is this issue also related to just spying (using wait) and not stubbing/changing the WS behavior? When I try to intercept the http GET to a websocket and then wait for it, it does not work and times out.
Use case: |
@mrpicklez70 Did you succeed in mocking graphql subscrioptions? |
I've been having a hell of a time getting this pattern working. I have multiple tests within the same file all invoking a similar pattern to what @kanteankit posted, but my tests kept bombing out seemingly because I had consecutive tests using the pattern. I was seeing errors similar to this when running in headless mode. What ultimately fixed my issue was passing the |
@bahmutov asking you as I was unsuccessful resolving the issue. Hoping you will have some insights. I have an app that uses both XHR requests and web socket connections (mainly to receive messages from the server). Web socket connection is implemented with However, I am seeing a weird behavior where mocked message (sent by the server) is not being processed by the socket client ONLY IF routes are not intercepted. If the routes are intercepted, message is getting processed as expected. As soon as I remove command to stub the requests, message disappears. Here is my setup: Websocket mocking
Code to make sure that socket is replaced with mocked socket if running cypress
Cypress test
|
Thanks to the help in this thread, I was able to write a Cypress plugin to deal with web sockets. You can check it out here: https://www.npmjs.com/package/cypress-mock-websocket-plugin Any feedback appreciated - especially if it's kind :) |
For those looking for a way to make SignalR work with Cypress: Cypress-SignalR-Mock Any feedback is greatly appreciated! |
Current behavior:
We can use
cy.server()
andcy.route(...)
to mock HTTP calls.Desired behavior:
The exact same thing for Websockets.
For apps using websockets quite heavily this would come really handy.
Might be related to #199
Would be great to have the ability to watch a bi-directional web socket to process messages from the back-end as they are received.
Use Case:
User preforms a action through the front-end which actions several back-end events. It would be great if we could monitor that these different events did occur (there is an endless amount of different events and combinations of events).
What would be great if there was a simple message pool/queue or a way to "monitor" a bi-direcitonal websocket and capturing the messages received from the back-end.
We would then write tests to check if those events are being executed by looking at the socket/pool/queue and seeing if the events were received after X user actions.
The text was updated successfully, but these errors were encountered: