-
-
Notifications
You must be signed in to change notification settings - Fork 535
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
Support concurrent runs in Node.js #474
Comments
If I use multiple instances of It's very strange that nock is not having that problem, their restore function is very simple and don't have any kind of check 🤔 |
Thank you for the insights, @marcosvega91. I suppose we can dedupe modules patching in What we can investigate is how exactly modern test frameworks execute tests in parallel. For instance, do they provision forked processes? If so, perhaps we could bound request interception to the forked process instead of the parent process. |
I have tried to investigate today a little bit. it seems that |
@marcosvega91, thanks for investigating that! I believe that the memory leaks were fixed in mswjs/interceptors#77. Regarding the patched modules reset, it's indeed peculiar, as MSW resets those patched modules in a similar fashion to nock. |
@kettanaito @marcosvega91 Seems like we need to deeper investigate, why we have a memory leak in When it comes to the code, where do you think I can perhaps start by in order to deeper investigate it? @marcosvega91 How can I reproduce this memory leak issue in our current Repo with the tests we have? Also, you mentioned we do reset those patched modules in a similar fashion to nock, where in the code are we doing that, I am looking for some sort of direction. Thanks, haha, I love working with you guys 😃 🥰 🤗 |
@tigerabrodi, thanks for the interest in this task. I'll try to share some details below. Restoring patchesEach interceptor in NRI a function that, when called, patches a respective request issuing module to intercept requests, and returns a function to restore that patch. Below you can find the restoration points for two current interceptors:
|
@kettanaito @marcosvega91 I do not know I will ever get to this issue, am personally fully up with the OSS Raid Group aside from my side project + workshops at FrontendMasters. |
Hi there! That's my first time here in the MSW repo, so first of all, congrats to everyone involved; the project is fantastic. ⭐ I've started to learn more about the library's internal and come across this issue, so I'm trying to understand if I should run my Jest tests using the Considering what I read here in this issue and my current understanding of Jest's parallelization strategy, I would expect it to be OK to run multiple Jest test suites in parallel. In practical terms, I would expect it to be acceptable to use Wrapping it up, if I create a single I would appreciate it if you could help me to clarify this. Thanks! |
Hi, @fnmunhoz. Thank you for the feedback. I'll try to clarify things for you below. Despite the parallelization in Jest, it still only spawns 1 process. The parallelization may happen via threads/child processes to achieve the execution of multiple test suites simultaneously. The request interception functionality in NodeJS for MSW patches the request issuing modules per process, so regardless of the parallelization it will affect unrelated tests, as they all run in a single process. We could use some help in researching how exactly Jest implements parallelization. It won't give us a solution, but it may hint us as to the right direction. I believe the solution lies in handling parallel runs in |
Trying to simulate a potential undesired behavior of MSW as described in mswjs/msw#474
Hey @kettanaito thanks for the update! I'm trying to simulate this problem in Jest, but until now I'm not able to make tests from different Test suites affect each other. I'll share what I have tried, as you might spot what I might be missing. I've created this repo with two identical test suites except for fnmunhoz/msw-concurrent-execution@67f7cf0 The Jest output for that is as in the screenshot: By its logs, I couldn't see any issues, since each Test suite seems to respect its own HTTP handlers. Do you think the way these test suites are defined should demonstrate the concurrency issue? If not, do you have any suggestions on what I could try? I'm in hope that since Jest executes each Test suite in a separate process, it might not be an issue if we use MSW with Jest as the test runner? |
Does this mean that I should run tests in band ( |
@JCB-K at the moment we do recommend running them in band to prevent possible issues when request handlers affect unrelated tests. This issue is aimed at discussion and adding support for parallel test runs. That being said, it looks like not everybody experiences this issue, so there's a space for some setup difference being at play. |
Oppgradert til Jest 27 som krever at vi setter jsdom for frontend tester, delt msw handlerene i de som kreves i frontend og de som kreves i backend, slik at vi kan ha færre mocks samtidig Det er en kjent issue at msw ikke er veldig god på concurreny: mswjs/msw#474
I'm experiencing this issue now. This is what I've found so far. As far as I can see It does however use a worker pool, where each test file is given to a worker. All tests within that suite will use the same worker process. Therefore a global If you have more tests(files) than workers, jest will reuse workers (but still run beforeAll for each file). Regardless of I can reproduce this issue quite frequently while running with A common denominator in my case is that I have three tests in a file. One happy path where it uses the globally registered mock handlers, and two unhappy paths where one is experiencing a delay (to verify that the spinner is shown) and one where there is a network error (to verify the error message). Those two tests registers their own handlers on the same endpoint/URL. They seem to occasionally intersect with each other, that is, the spinner tests gets the request with an error, and the error get the delayed request. |
I have found two things in my test suite that creates the flaky behaviour. Simply clearing the cache like this does not help (all the time):
When adding a artifical delay like this, it seems to consistently work:
I suspect this might be connected to the other thing I've found, but not been able to prove/create a consistent reproducing case. I test a component that uses SWR to load data, but in this case it can render instantly before the fetch is complete and update it as the data comes in. I have no way to differentiate the loading status from an empty dataset, so the test passes immediately. But the request (and the mock) is still in-flight and active, so the next test (which should have data) hits the previous mock. Workaround was adding an invisible loader that I can wait for to disappear before passing the test. |
@androa I suspect I’m also having a similar problem to you. I tried adding an artificial delay (without cache clear) and it didn’t work. I didn’t consider using invisible loader which is a good solution so that in the test you can determine when the request has completed. In many of our tests when the api completes it doesn’t result in a screen change. There’s also many tests where an api call is made but the test doesn’t need to wait for it to complete. I want to spend some time to create a reproduction repo but haven’t got round to it. Hopefully I can find some time soon. |
What is the use case and requirement? |
@guest271314, the use case is to ensure the isolated application of runtime handlers (overrides) when running multiple tests relying on the same handler in parallel. The requirement is, well the same. I think the comments above will give you enough understanding about the issue. |
If I understand the requirement correctly, it is to run multiple instatnces of your tests at the same time, "paralellism"?
|
@guest271314, let me clarify a thing or two. Test runners like Jest usually support "parallel runs" what they do internally is not a concern for this ticket. The end result is that there may be X amount of test files running in parallel. Those test files have test cases. Those test cases can make HTTP requests. Some of those cases may want to handle those HTTP requests differently. Here's a scenario for you to get a grasp of the issue: // testA.test.js
it('fetches user detail', async () => {
await fetch('https://api.example/user')
}) // testB.test.js
import { rest } from 'msw'
it('handles user errors', async () => {
server.use(
// For GET https://api.example.user request ONLY within this test
// respond with a 500 response.
rest.get('https://api.example.user', (req, res, ctx) => res(ctx.status(500))
)
await fetch('https://api.example/user')
}) MSW already has request interception, runtime overrides via This very closely depends on how a test runner implement parallelism. For example, if those two tests run in separate threads, then each of their There quite a few examples of this above, please read them. People go into much better detail of what's happening that I do in this comment. |
I think the code I posted achives the requirement - without bothering with Just append N |
@kettanaito Would it make sense for |
@kettanaito is there any workaround for that? |
Is there any work going on with this? Being able to override responses for a given endpoint in tests confidently is basic to using |
@clarksam19, yes, scoping runtime handlers to the module ID would work. The problem is where to get that module ID? I believe I tried @lluia, you can provision overrides per test even now via |
First things first, thank you for the amazing work that you are doing. I find msw amazing! On top of that, is there any workaround until there's a proper fix for the issue? We're struggling a bit with the flakyness of our test suites as we use runtime handlers to test sad paths. Does it make a difference if we create a server per test file? beforeAll(() =>
server.listen({
onUnhandledRequest: 'error'
})
);
afterEach(() => {
server.resetHandlers();
});
afterAll(() => server.close()); |
@EduardoSimon, thanks for your kind words. The only workaround at the moment is disabling parallel runs in your tests. If you're using Jest, that would be adding I think what @clarksam19 proposed has the most chance of being the right solution. I tried playing with some sort of One thing to try, is to check whether Line 56 in e87e00b
I think as long as its evaluated upon the function call, it should point to the directory of the module calling it. |
…in multiple tests see mswjs/msw#474 (comment)
Regarding coloring the requests happening in different test files or even Once again, the main obstacle here is that the |
UpdateI've got a basic version of concurrent request resolution working using This has a couple of concurrent tests in Jest running across multiple workers and using a minimal version of It utilizes async context with each How this worksThe issue itself is that MSW keeps an internal list of Async storage solves this problem this way:
|
On
|
Introducing
|
it( | |
'resolves request against the in-test handler override', | |
server.boundary(async () => { | |
server.use( | |
http.get('/initial', () => { | |
return HttpResponse.text('override') | |
}), | |
) | |
const response = await fetch('/initial') | |
expect(response.status).toBe(200) | |
expect(await response.text()).toBe('override') | |
}), | |
) |
The idea is simple: you wrap whichever scope you want in server.boundary()
, and no changes to request handlers will ever leave that boundary. You can wrap an entire test, or its part in that boundary, enabling isolation of network behavior.
Now, this is an explicit choice. Running tests in parallel is also an explicit choice so I see these two coming hand in hand.
The existing behavior of server.use()
will not be affected. It will behave the same and will keep the request handlers in-memory. This is great for sequential test runs (most test frameworks don't run tests in parallel so you are getting a great default).
I already have tests passing for this feature and I think it is the way MSW will support concurrent test runs. Please share your thoughts on it below and help us shape it!
Important
You can help with reviewing the implementation: #2000. Thank you!
Released: v2.2.0 🎉This has been released in v2.2.0! Make sure to always update to the latest version ( Predictable release automation by @ossjs/release. |
Using MSW in concurrent testsMSW can now be used in concurrent tests via the |
What
I suggest for the
setupServer
(node-request-interceptor
) API to support execution in a concurrent mode (i.e. multiple test suite at the same time).Why
Concurrency is crucial in large projects, allowing for faster execution of tests.
Current behavior
Currently
node-request-interceptor
patches native request issuing modules per process, which allows irrelevant mocking rules to leak to another test suites, as they all run in a single process.How
Look into how
nock
operates. It performs modules patching in a similar fashion, and I'd suspect it to support concurrent mode. We can learn how they did it and evaluate if we can adopt a similar solution.The text was updated successfully, but these errors were encountered: