Mocking WebSockets with MSW
Artem Zakharchenko introduces Mock Service Worker (MSW) API support for WebSockets. A detailed discussion about the use and benefits of a mock environment in the initial stage of development was discussed, with Artem emphasizing the enhanced control it offers over WebSocket events.
The concept of mock-first development was introduced as a way of allowing developers to start work in an environment where the server has not yet been established, with the assistance of MSW. This approach was further unpacked, exploring how it could provide significant benefits when dealing with WebSocket events, which cannot be intercepted using Service Worker API.
Artem demonstrated how to use MSW to develop a WebSocket application. The demonstration involved real-time user interface interactions and event handling. In transitioning from mock to full pass-through mode, MSW can simulate a duplex connection similar to normal WebSockets.
A feature of MSW that was highlighted is its ability to observe and modify server interactions. Through setting up a message listener, a developer can manipulate values coming from the server as well as the client. Artem further noted that MSW is the first API mocking library in the JavaScript ecosystem to support both HTTP and WebSocket protocols.
The talk concluded with an invitation for audience engagement and contribution, encouraging developers to use the pre-release version of MSW and provide feedback.
Share this talk with your friends
Transcript
Yay. So we're here. Wow. Hey, hello everyone. Thank you so much for being here. Let's start.
So my name is Artem. I'm a software engineer and just recently I've joined Epic Web with a bunch of other fantastic instructors to teach you all about the automated testing. And in fact, I'm head deep into editing the very first workshop on the testing fundamentals
and you're going to hear about it very soon. And today, I'm really, really grateful and honored to be part of the very first Epic Web Conf. In fact, let's give a warm round of applause for everybody who made this conference happen. It's epic. And I thought this occasion
deserves something epic from my side. So, I decided to invite you to play a little game. Oh, no, no. No, no, no. Not another game. This game. This is the game my wife and I built and this is a tug-of-war game. And right now, we will all play together.
You will help this grumpy dwarfs of Utah find answer to the most important question of humankind, which is tabs versus spaces. Okay? So, joining is very simple. Just scan this QR code with your mobile phone, put it in the landscape mode. I'm just going to do the same. All right.
Yeah, then you're going to choose a site depending on what you believe in. So, left or right. And then when the game starts, just click on that side of the screen anywhere and you will be helping your team pull the rope. And obviously, the team that crosses the middle
or even gets closer to the middle is the one that wins. Okay? So, we're having some players, 20 players. Let's go. Oh, yeah. I hope I can still start the game, so maybe
stop joining. So, all right. I'm going to load it. So, is this loading? Come on. And
we broke the Wi-Fi. Okay. So, will we have the game? Because there's an iFrame and we
were about to broadcast it live. Yeah. I think we even broke the cellular. It doesn't
connect for me. Okay. Let's give it maybe one more try. Damn, I was really hoping we're
going to find out the answer. Okay. Yeah. Okay. Whatever. So, this one. You can play
this game by yourself after the talk. Sorry, you know, technology. This is basically the state we're in. So, I built this cool real-time application to play, but it's quite difficult. As powerful as these kind of apps are with collaborative editors and chats and multiplayer
games. Oh, but I do not have LAN, so that's okay. That's okay. Don't worry. Yeah. So, as far as amazing as apps are, they're quite difficult to build and test and debug, and it becomes essential that we have the tooling to help us do all that. And, you know, you
know where I'm going with this. So, do you know this logo? Are you familiar with MSW? Yeah? Nice. So, MSW is an API mocking library. So, let's take a look how we can mock WebSockets with MSW. So, I have this app right here that's, thankfully, running. And let's just have a
brief tour of how it works. So, we have a WebSocket client, and it connects to the actual WebSocket server. I'm using WS and notes. This is a remix app. And as any WebSocket application, I'm handling events coming from the server, like the state change event or maybe a score event and so forth, and then I'm updating the UI. That's pretty simple.
So, how can we approach this in the mock world? So, let's try developing this app mock first. So, the server doesn't exist. We forgot about it. What we can do, we can import this new WS object from MSW, which stands for WebSocket, and we can call WS.link, which will create
this kind of interceptor for this WebSocket server URL. And this is not a handler just yet. And to make it a handler, you'll go to your handlers array like you used to, and you will add a listener, connection listener, that has a callback. And now, whenever any
WebSocket client in your app connects to the server that matches this predicate, you will have this listener called. And in response, you're going to receive this client object, which is a representation of this WebSocket client. So, even now, you can see in the console, I have MSW running, and I have these two logs, two connections. So, one is the actual game,
and the second one is this little admin panel I have at the bottom to help me basically manage the game. All right. And here's the thing, WebSocket events cannot be intercepted using a service worker API. So, what we have to do in MSW is to patch the WebSocket class, the global class, to have the interception capabilities. And that's kind of not ideal
because you won't be able to observe this network in your network type of the dev tools. So, what we're coming up with is this nice little logging that's still going to let you know what's happening, and you're going to observe it later as we experiment. So, here, we just see the connections happen. Nice. But nothing is happening in the game because the game itself actually expects the server to let it know that, hey, there's enough players
we can start playing. So, it expects an event. So, let's try to send it from our MSW event handler. So, to do that, we can call client.send. And you need to remember that we're writing these handlers from the server's perspective. So, this will send the data to the client. And with this, you can see that, hey, it actually receives an event, which is a state
change event, and you have this nice message event reference, which you don't get in your dev tools. So, that's a win. And it has some byte length and the string representation of the message sent. So, that's nice. And now, once I pull on the side of the screen, this is what you were supposed to see if we had Wi-Fi. But you're basically starting to
sending an event from the client, which is a pool event that lets the server know, hey, this is the right team or the left team pulling on the ropes. So, please calculate the score. Who is winning? This is really nice, but nothing is happening visually because we need to teach our handler to react to this event. So, in fact, to intercept any events from
the client, and this client object is event target-based, just like WebSocket instance. This is event target-based. So, you just add a listener, the message listener, and you get a message event. So, you can observe any events coming from the clients. For example, like here. Yeah, here's the console log of this pool event coming out. This is really
nice. So, let's try to respond to this event. So, I will have a similar listener that intercepts this event, parses this message, and then if it's a pool event, we will send back from this event handler. Let's send back a state change that actually whichever team pulls on the rope wins immediately. That's pretty nice. So, we're going to change it, send this
game and state with the team that pulled the rope immediately wins. So, when I do this, okay, space is won immediately. And the same will happen if I try to pull on the taps team. This is really nice. And I remind you, this is the actual WebSocket app communicating with the event handler as the server. So, all the logic you have in the UI, that's how
you test it, that's how you debug it. That's pretty cool. So, what else can we do? This is the bare bones of the MockFerse development and this is the default mode that MSW ships when you want to develop your WebSocket applications. But what if you have an existing WebSocket server? Well, you can do something else. In this connection listener, you also have
a server object and it represents the actual server connection. So, you can, let me just remove this one. We can call server.connect. And once we do, we will enter a full pass-through mode. So, all the outgoing events from the client will be forwarded to the server and
all the server events will be forwarded to the client. And MSW will just sit in the middle. And this is really interesting because at this point of time, it's no longer a mock server, it's kind of a proxy. So, you can have a duplex connection just like you have with regular WebSockets. So, let's give it a try. Once I start the game, yeah, I can actually see the events coming from the server. This is actual WebSocket server trying to
communicate and calculate who's winning. So, let me just let the spaces win. This is not opinionated. So, yeah, that's it. We observed the app working with the actual server but MSW was guiding us throughout the whole way. We can observe all this interaction. And since this is the actual connection, you can also see it in the network if you want to. So,
let's have fun with this. So, we can observe this interaction but let's also meddle with it a little bit. So, I have this message listener for the client that's going to detect the pool events again. And then, it would be really nice if there was a way in JavaScript to prevent
the default behavior of events. And, yeah, obviously, we're going to call event.preventDefault and this is going to cancel the forwarding of client messages to the server. So, these pool messages will never reach the server, ever. And instead, let's send back this three
identical events to the actual server, just forwarding them. This basically means that one pool will equal three actual pools on the server. So, let me try this. Let's start the game. And once I root for this team, yeah, this is so strong. Basically, what we did
right now is mocked extremely strong dwarfs. And this is nice, but what if we want to do something with the server sent events, events coming from the server? So, in the same fashion, we can have an event listener for the server for the message event. So, when the actual
server sends back the game score, and this is the number basically deciding which team is winning, let's try to also prevent this forwarding. So, client never received this actual event, and we grab that score, and let's kind of flip it upside down so spaces
can never win. Yeah. So, with this, I'm going to start the game, communicate with the server, but if I try to, actually, spaces are pooling themselves. And this is actual events coming
from the server, but I'm just flipping the data. This is so, so nice. So, this is just a glimpse of what will be possible to do with WebSocket APIs with MSW. And, oh, no, no. And the next slide has a sentence, and I've been meaning to say that sentence for at least
a couple of years. So, I think this is a good occasion. With the introduction of WebSocket API mocking, MSW becomes the first API mocking library in the entire JavaScript to support both HTTP and WebSocket protocols. And that's epic. And not just support, but do that in
the familiar way, being environment and client-agnostic, where you can describe your network once and integrate it across the entire stack for testing, development, debugging, basically anything. Even now, you can use this API to intercept raw WebSocket protocol connections, but also
Socket.IO and GraphQL WS. And very soon, we will ship a designated first-class API for GraphQL subscriptions. So, you can mock and do everything with GraphQL subscriptions when you're building the apps. The best part about it, you can try it right now. You can just
run NPM install MSW at next because this is not a public release just yet. It's a release candidate. So, it's crucial for me to ask you to give it a try, to try it out, to experiment, to give me the feedback. And don't worry if you never build a WebSocket application before. This is actually a perfect opportunity to try. Just build your app and let it speak
with MSW handler as a server and let me know how it goes. And, of course, if you like MSW, if you believe in what we're doing there, consider becoming a sponsor. It would be incredible. Talk to your CTO, talk to your manager, and let them know how MSW helps you ship and test
products better. And if it doesn't, you probably want to talk to me, so we can improve it. Just some six months ago, I was laid off from a full-time job. And instead of finding yet another place where I can bring value, I decided to focus on the place where I already bring value, which is my open source. And since then, just in the last four months, together
with fantastic contributors, we were able to ship two of the most anticipated features of MSW. And I'm very, very excited about what comes next. In fact, I know what comes next, but I'm not going to tell you just yet. So, yeah, consider becoming a sponsor. It would mean a lot. Thank you.