Settings

Theme

MSW 2.0 – Mock Service Worker

mswjs.io

188 points by milkers 2 years ago · 60 comments

Reader

dcre 2 years ago

At my job we've found working with MSW + OpenAPI to be near miraculous. I work on a web frontend and do most of my development against a mock API powered by MSW. This live preview runs against the mock API running fully in-browser.

https://oxide-console-preview.vercel.app

More details:

https://github.com/oxidecomputer/oxide.ts

https://github.com/oxidecomputer/console

https://oxide.computer/podcasts/oxide-and-friends/1426644

Really excited about this next step for MSW — we'll be upgrading soon. Building on web standards buys you so much.

(Reposted at top level because the parent got flagged.)

  • t1mmen 2 years ago

    Really cool setup, thanks for sharing!

    After buying in to OpenAPI as the fundamental source of truth (generated via https://www.ts-rest.com contracts in my case), I have radically changed how I think about web development.

    At this point, it's frankly absurd to me how many people out there make it so hard for themselves by manually building/typing/validating both client & server-side responsibilities in most typical REST setups. I get it -- I spent >2 decades in that reality, but no more. I will die on this hill.

    I am likely understating the impact when I say I'm 2x as productive whenever I touch API related functionality/validation on client or server-side.

    MSW, Zod, react-hook-form+ZodResolver and several other tools in the OpenAPI ecosystem are simply incredible for productivity. The project I'm on now is certainly the most fun I've ever built because of these amazing tools.

  • adamontherun 2 years ago

    we started using (and now contributing to) https://orval.dev/ this year which both generates the mocks using MSW as well as the client-side networking code (React Query in our case). It removes so much boilerplate its amazing.

    wrote up the basics of our workflow few weeks ago https://betaacid.co/blog/api-contracts

    • jcullen 2 years ago

      Orval is great, I also recently wrapped up a project with Orval, React Query, and MSW. Generated my typescript models, helped me track changes in the API each time I updated against the swagger spec and let me test and demo the UI before our API team had a server running. Such a time-saver.

  • wdb 2 years ago

    Yeah, @msw/source is pretty sweet for that :) Use a .har or OpenAPI spec to create mock handlers

  • ushakov 2 years ago

    Amazing. At Step CI we’re currently working on a tool that will do the conversion automatically (OpenAPI > MSW)

    Feel free to email me at mish@stepci.com if you want to hear more!

    • dcre 2 years ago

      We more or less did that too. We generate typed wrappers for every handler, and then we manually implement the internal logic for each endpoint. I doubt reliably generating 100% of the logic is possible, but even if it is, I would guess it's more work to do that than to do it manually.

      Generator: https://github.com/oxidecomputer/oxide.ts/blob/64401fa2/gene...

      Generated output: https://github.com/oxidecomputer/console/blob/8e74accf/libs/...

      Manually implemented endpoint behavior: https://github.com/oxidecomputer/console/blob/8e74accf/libs/...

      • zellyn 2 years ago

        I'm so glad to see all of your comments here. I've been fascinated by your setup ever since hearing about it on the Oxide and Friends podcast. The part that I'm most curious about is the part where you weave together auto-generated interface code with hand-implemented mock implementations… I'll have to go read your code and see how that happens.

        [Edit, after reading a bit of the code]

        Am I correct in understanding that the generated code maps API calls to a typed interface, and then the hand-built implementation simply implements that interface?

        So rather than complex weaving where you want hand-coded function bodies but generated signatures, you have generated signatures, where each function body calls a method on a generated interface.

        • dcre 2 years ago

          Yeah, basically. We do it with a function call where the argument to the function is that interface representing all the API endpoints. `makeHandlers` handles parsing path params, query params, and request body and passes them to each endpoint handler. So the runtime validation of request bodies is also generated — we generate a zod schema for each request body in the OpenAPI definition and use it to parse the actual request body that comes in. So it's not just a generated interface — there is also generated runtime behavior that is endpoint-specific.

          big function call https://github.com/oxidecomputer/console/blob/bd65b9da7019ad...

          automatic body parsing and argument passing: https://github.com/oxidecomputer/console/blob/bd65b9da7019ad...

          When an endpoint gets added to the spec, we can rerun the generator and get type errors in the `makeHandlers` telling us endpdoints are missing.

    • wdb 2 years ago

      Why not use @mswjs/source?

      • adam_beck 2 years ago

        I've seen you mention this a few times but can't seem to find any information on it. I'm very interested in being able to use HAR files with MSW.

      • c01nd01r 2 years ago

        Could you provide a link to this package? I can't seem to find anything.

  • berziunas 2 years ago

    Is it ok if I see I am logged in as as Hannah and can launch new instances after clicking the first link?

    • jonchurch_ 2 years ago

      It's all mocked, so you're not really Hannah, and you're not really launching instances. Their console is open source, and when you run it in dev mode locally this is also what you see.

      • gregschlom 2 years ago

        Also, worth pointing out that Hannah Arendt (the name that shows up as the logged in user) is a famous historian and philosopher (https://en.wikipedia.org/wiki/Hannah_Arendt)

      • refulgentis 2 years ago

        Just when I think I get the whacky web kids, hell, that maybe I've even one of them, an article like this and functionality like this comes along.

        I don't know how a mock library enables a revolution of having test data.

        I don't know why the library couldn't change it's favorite method signature because a subset of versions of one JS framework couldn't...fetch?

        or why the mock library can only have one dependency...

        ...and I feel _really_ out of the loop because I can't understand the tone, hinting at years of sweat and though that I'm more used to from a consumer product launch.

        But that's why I respect the web more than ever. The reaction is real, and it means something, even if I don't understand it. People put _years_ into making (gestures) this work, so millions of developers can benefits, so billions, hell, _humanity_ can benefit. All in the open.

        Shine on, you crazy diamonds. https://www.youtube.com/watch?v=54W8kktFE_o

        • dcre 2 years ago

          Worth noting that in my experience it's the combination of MSW and code generated from an OpenAPI spec that really makes it special, not just MSW alone.

          The other reply covers it but I want to slice it slightly differently. I see two things that are different here. One is mocking at the HTTP boundary rather than mocking, e.g., function return values or modules or whatever. That's certainly not revolutionary (I know people do it with rspec and I'm sure plenty of test frameworks do it too), and in theory you could build your mock server with any tool that lets you build a server. But people don't usually do this, and when they do they tend to do it bit by bit for each test rather than thinking about it as mocking the entire API. So for example, in my mock API, my POST creating a project actually creates a project in a mock database, and then when I list projects, I can see what I just created. So I can test the UI by doing exactly what you would do manually — create the thing and see that it shows up where it's supposed to. All of that is built independently of my test for that feature, and it works for all the tests I write — I don't have to think about mocking API bits for a particular test.

          Worth a read: https://kentcdodds.com/blog/stop-mocking-fetch

          The other thing that's unusual is running this mock in a Service Worker, which means it can run in the browser. That's pretty cool and pretty unique to this tool. But the Mock part is about running the same server outside of the browser i.e., in Node (or now Deno or Bun), which means you can use it for unit tests. So I think the thing you have to see here is the synthesis of all that in one thing — it's one mock server that (because it's written in JS) can run both in the browser and on the server. I admit it doesn't sound that great until you try it, but it enables some cool and surprisingly useful stuff like the live preview I linked.

          • refulgentis 2 years ago

            Thank you __very__ much, I went a bit long to try to signal I was serious and thought I wouldn't get any substantive reply. This is beyond my wildest dreams. Thanks again.

        • afavour 2 years ago

          > I don't know how a mock library enables a revolution of having test data.

          From my understanding this isn’t (just) mock data, it’s an entire mock backend that runs inside the browser. To be able to simulate that with zero dependencies outside the browser does simplify a lot of testing scenarios.

candylifter 2 years ago

Nice to see more and more tools adopting standard Fetch API Request/Response interfaces.

  • flakes 2 years ago

    Agreed. The more non-intrusive your APIs are, the better the overall experience is going to be for developers. Diverging from official/standard APIs means higher chance framework lock-in, which has kept me away from a lot of tooling.

    Always try to ask myself when adding tooling: What's the chance I'll want to remove this later, and how painful is that going to be?

scottlamb 2 years ago

How do folks test timing-related stuff with MSW? AFAIK, MSW doesn't get along with jest.useFakeTimers. It drives me nuts; I have a bunch of disabled tests in an open-source project and at least one that is flaky because it uses real timers where I'd like to be using fake timers. [1, 2]

I've been thinking about ripping out MSW from my tests in favor of my own API-level mock for this reason. But it seems like many other folks are happy with MSW. I have to assume there's something I'm not getting. I'm a fish out of water with frontend stuff in general...

[1] https://github.com/scottlamb/moonfire-nvr/blob/5ea5d27908f1a...

[2] https://github.com/scottlamb/moonfire-nvr/blob/5ea5d27908f1a...

IggleSniggle 2 years ago

Super excited to see this land. I've been following this closely and experimenting with the beta branch; the ability to swap out real network requests with mocked ones in process is a huge upgrade for any kind of work you might have wanted to do with a proxy.

Congrats on the release!

danwee 2 years ago

Off-topic: I think I've seen this page layout before https://mswjs.io/docs . Did they use a css-html template/framework for it?

milkglass 2 years ago

Why does the site need us to allow ads? On the getting started page I see this:

"Please enable ads on this website. Thank you."

  • ranting-moth 2 years ago

    They're probably trying to make some money for their work that people take for granted. I don't like ads, but I can't blame them.

  • wccrawford 2 years ago

    If I enable ads, it shows an ad for an ad service to target developers.

orthecreedence 2 years ago

For a second I thought it was Magic Seaweed 2. RIP.

ok123456 2 years ago

What is the benefit of using MSW over mocking a service with a small express server?

  • ljm 2 years ago

    You can generate TS from an OpenAPI spec and then create type-safe mocks of network calls.

    Basic mocks and fake servers run the risk of falling out of sync and giving false positives, or just being outright wrong to short-cut some of the work. It's also less code to maintain when a service worker can intercept the call, instead of orchestrating a load of mock APIs.

    It won't stop you making breaking changes on the API but it will keep you honest on consuming the API on the client.

    • Klaster_1 2 years ago

      Too bad this only gets you so far. Last time I checked, the OpenAPI specs are not as flexible as TS types and the people who author these - probably the team members responsible for the backend - won't necessarily even be able to list all the invariants due to framework and host language constraints. And OpenAPI does not support WebSockets. The project I work on has a frontend written in TypeScript, a Java backend, an Express-based dev-server that shares same types with FE, but none of the solutions the team evaluated enables the type sharing across all three beyond simple "an object can have these fields" - no algebraic data types, no WebSocket support. In the end, we resorted to agreeing on as much as possible in plain text before greenlighting the new endpoint, and then double checking the implementation and client usage for unexpected behaviors.

      • ljm 2 years ago

        For those use-cases you might just be better off with either contract tests or simply making the payload itself typesafe with something like protobuf.

        OpenAPI/Swagger is basically inextricably bound to REST/JSON APIs over HTTP. I also think they're quite abstruse in terms of defining non-trivial interfaces.

  • meiraleal 2 years ago

    No need to run a small express server? MSW runs in the browser so you can develop against a mocked API.

  • szines 2 years ago

    One of the biggest advantages of MSW, is that you can use the same mock server in your unit tests (jest) on Node.js environment and run the full frontend in the Browser for development and preview.

    The best if you have totally the same mock data, so super easy to debug your tests.

    Also, you can use this trick to have scenarios, so you can demo different behaviours just by passing a query param. QA loves it as well.

    https://github.com/zoltan-nz/meetup-contacts-app-2021/blob/m...

  • dcre 2 years ago

    For me, the fact that every dev server tab I open is its own fresh server instance is really useful. We can run our tests in parallel with no server. We can deploy a live preview as a static site.

    https://oxide-console-preview.vercel.app/projects

  • abalashov 2 years ago

    I have the same question. I just use a small local container fleet (Docker Compose) and have my actual API server running, and have never seen this to be a problem. I can do all this local development without depending on connectivity, etc.

Keyboard Shortcuts

j
Next item
k
Previous item
o / Enter
Open selected item
?
Show this help
Esc
Close modal / clear selection