httptoolkit / mockttp

Powerful friendly HTTP mock server & proxy library
https://httptoolkit.com
Apache License 2.0
775 stars 88 forks source link

Access the same mock server instance from 2 different clients #126

Open stigkj opened 1 year ago

stigkj commented 1 year ago

I have a Next.js app that uses an external API when server-side rendering the page and Cypress is used for e2e-testing.

The external API must be mocked and Mockttp looks like a great fit! To make this work, we start the Mockttp server first in a Node.js script, i.e. both admin and remote server, and then uses the remote server's url property to configure the HTTP proxy when starting up the Next.js app.

With this I can setup the mocks in the Node.js script and the Next.js server-side rendering code will use these mocks.

Then I start Cypress and need to add new mocks from the test code.

Is it possible to connect to the already running Mockttp "remote" server started in the Node.js script from the Cypress test code? I know I can start a new Mockttp instance from Cypress, but that wouldn't work as I cannot change the HTTP proxy in the already running Next.js server.

It would also be possible to only start the Mockttp admin server in the Node.js script and then start the Mockttp remote server from the Cypress code.

There is a couple of issues with this, though:

stigkj commented 1 year ago

A small drawing of the setup:

sequenceDiagram
    participant C as Cypress
    participant N as Next.js server
    participant M as Mockttp
    participant E as External API
    N-->>M: Setup/start mock server
    C-->>M: Add/change mocks
    C->>+N: Real request to page
    N->>+M: Intercepted request
    N-->>E: The request would usually go to an external API
    M->>-N: Response to request from mocks
    N->>-C: The server-side rendered page
    C-->>M: Validate mocks have been used
stigkj commented 1 year ago

Another issue with starting the Mockttp remote server from the Cypress code is that the Next.js server might have something that runs "in the background", i.e. independent from and possibly before the remote server is started from Cypress. This code will then fail when trying to access the network.

pimterry commented 1 year ago

Interesting, yes I've heard similar questions before, but there's no good answer yet. As of today, the best solution is indeed to use a fixed port for your proxy URL, and then start & stop a mock server on that port within your tests.

As you point out, this does require duplicating mock setup between tests, but you can easily refactor that into a helper function, and it can often be useful to do that anyway, to give the tests full control over all mock behaviour, and make it clearer exactly what is mocked in which test. Fully recreating the mocks in each test also has the advantage that you definitely have a clean slate every time, which provides much better guarantees against interference between tests.

There's a similar Cypress-related discussion and an outline of how to do this nicely that you might find useful here: https://github.com/httptoolkit/mockttp/issues/62

All that said, that's just the state of play today. I am interested in expanding this to fully support headless/persistent usage in future - i.e. where a server is created in one place, and then independently externally accessed and controlled elsewhere, without the current tight link between the server & client, and allowing a server to be created, configured, and left to handle traffic with no client connected at all.

The challenge here though is that there are a few interactions that currently expect persistent connections, for example callback rules (.thenCallback(() => ...)) actively communicate with the client to ask it how to handle every single matching request, which won't work as expected if the clients change, or if sometimes there are no clients connected at all. Similarly, clients can subscribe to events, which creates a continual stream of messages from server to client, which needs thought on how to handle client disconnection and/or the possibility of multiple clients being connected in parallel. It's also normally better to aggressively shut down servers, to avoid ports being used unexpectedly & indefinitely if a client crashes.

Anyway, if you're really interested in this and the current state of play above doesn't work for you, I'd be happy to see progress here! Best to share an outline of how you'd like this to work first, so we can discuss the theory before actually creating a PR, but feel free to share ideas or suggestions here.