msw
msw copied to clipboard
React Native Msw And Axios Not Working Together
When I make requests to msw handlers, axios returns empty strings as data for some reason, but when I make requests with fetch, the data comes correctly. when I check if the handlers are triggered, the handlers are triggered. When I make a request to another api I get data with axios. can you help me to solve the problem?
index.js
import {AppRegistry} from 'react-native';
import App from './App';
import {name as appName} from './app.json';
async function enableMocking() {
await import('./msw.polyfills');
const {server} = await import('./src/mocks/server');
server.listen();
}
enableMocking();
AppRegistry.registerComponent(appName, () => App);
server.js
import {setupServer} from 'msw/native';
import {handlers} from './handlers';
export const server = setupServer(...handlers);
My Fetch Method:
const testFetch = async () => {
try {
const res = await axios.get('https://api.myMockApi.com/getAllProducts');
const data = await res.data;
console.log(data);
} catch (error) {
console.log(error);
}
};
handlers.js:
http.get(`${url}/getAllProducts`, async ({}) => {
console.log("test");
return HttpResponse.json(marketData);
}),
The Console Log Here Works Both In Fetch And In Axios
"axios": "^1.6.5"
"react": "18.2.0",
"react-native": "0.73.4",
"fast-text-encoding": "^1.0.6",
"react-native-url-polyfill": "^2.0.0",
"msw": "^2.1.7",
Node Version : v18.13.0
Not sure If I have the same issue but I'm encounter issue with nest.js module "@nestjs/axios".
This is some info about the console error.
● Notification controller › Notification controller in test mode › should call POST /api/notifications IN TEST MODE with type of LIVE_SESSION successfully
TypeError: Cannot read properties of null (reading 'readable')
● Notification controller › Notification controller in test mode › should call POST /api/notifications IN TEST MODE with type of LIVE_SESSION successfully
TypeError: body.getReader is not a function
at _NodeClientRequest.respondWith (../../node_modules/@mswjs/interceptors/src/interceptors/ClientRequest/NodeClientRequest.ts:555:31)
at ../../node_modules/@mswjs/interceptors/src/interceptors/ClientRequest/NodeClientRequest.ts:317:14
"msw": "^2.2.0",
BTW, it works fine if I return error
return new HttpResponse(null, {status: 400});
I have a same issue too...
And it works with fetch instead of axios.
I checked the headers, but in the both case, the headers were same like
{"content-length": "1", "content-type": "application/json"}
I think it's probably a problem with axios library.
I'm experiencing a similar issue, where it is not recognizing the axios post method, returning this error, TypeError: Right-hand side of 'instanceof' is not an object
Example post setup it is erroring on: axios.post("/api/reference-url", {id: p.id, searchStr: "some word"})
Version of MSW: "msw": "^2.2.2",
I also ran into axios-related issues with msw. My workaround is to mock axios to use fetch for the requests within my vitest suite. This should also be applicable to Jest (using jest.mock
instead of vi.mock
). Maybe that helps anyone in the future.
Solution that works for me in a test setup file:
beforeAll(() => {
// axios needs to be mocked to use fetch in order to work with msw
vi.mock("axios", () => {
const isAxiosError = (error: any) => error.isAxiosError === true;
const handleAxiosResponse = async (response: Response) => {
if (!response.ok) {
// eslint-disable-next-line @typescript-eslint/no-throw-literal
throw {
isAxiosError: true,
message: response.statusText,
response: {
status: response.status,
statusText: response.statusText,
data: null,
headers: {},
config: {},
},
};
}
const data = await response.json();
return { data };
};
const handleError = (error: any) => {
throw isAxiosError(error)
? error
: {
isAxiosError: true,
message: error.message,
};
};
const cleanUpParams = (params?: Record<string, string | undefined>) => {
return Object.fromEntries(
Object.entries(params ?? {}).filter(
(entry): entry is [string, string] => entry[1] !== undefined,
),
);
};
const getMethodHandler =
(method: string) => async (url: string, config?: any) => {
const queryString = new URLSearchParams(
cleanUpParams(config?.params),
).toString();
const modifiedUrl = `${url}?${queryString}`;
return await fetch(modifiedUrl, { ...config, method })
.then(handleAxiosResponse)
.catch(handleError);
};
const getMethodHandlerWithBody =
(method: string) => async (url: string, data?: any, config?: any) => {
const queryString = new URLSearchParams(
cleanUpParams(config?.params),
).toString();
const modifiedUrl = `${url}?${queryString}`;
const fetchConfig = {
...config,
method,
body: data ? JSON.stringify(data) : undefined,
};
return await fetch(modifiedUrl, fetchConfig)
.then(handleAxiosResponse)
.catch(handleError);
};
return {
default: {
delete: getMethodHandler("DELETE"),
get: getMethodHandler("GET"),
head: getMethodHandler("HEAD"),
patch: getMethodHandlerWithBody("PATCH"),
post: getMethodHandlerWithBody("POST"),
put: getMethodHandlerWithBody("PUT"),
},
isAxiosError,
};
});
});
I was stumped by this problem for a while.
I found that fetch was working fine, but axios just kept failing. The MSW server handler was not intercepting the network request and my axios.get() call failed with an 'ENOTFOUND' error as it couldn't resolve the sysaddrinfo() call on my dummy URL.
Eventually though, I did find a very simple solution that worked for me... I switched the import of setupServer from 'msw/native' to 'msw/node' o_0
"axios": "^1.6.7",
"react": "18.2.0",
"react-native": "0.73.5"
"jest": "^29.6.3",
"msw": "^2.2.3",
Just want to also chime in here and say I'm seeing the same issue where the body is undefined/null when using MSW, Axios, and React Native:
"react-native": "0.73.6",
....
"apisauce": "^3.0.1", // uses axios under the hood
"axios": "^1.6.8" // bringing it in just to test with
....
"msw": "^2.2.9",
Fetch works fine! I've followed the basic set up steps in repo. I see similar comments: https://github.com/mswjs/msw/issues/1775#issuecomment-1937308589 and here: https://github.com/mswjs/msw/issues/1926#issuecomment-1937017406 (same user)
It sounds like it's somehow related to the XMLHttpRequest interceptor/stack...I do notice that it seems to be hitting the /browser codepath, and not the /node/native codepath (if that matters).
Same problem here. Using MSW with Axios in React Native doesn't work. The request response is not finalized and does not return the JSON as it should.
Hey @kettanaito
I've set up a basic reproducible example here (as simple as I could): https://github.com/zibs/mvce-msw-rn if you have the chance to take a look. The warning that is thrown in the video is " Cannot retrieve XMLHttpRequest response body as XML: DOMParser is not defined. You are likely using an environment that is not browser or does not polyfill browser globals correctly.", but I don't think is totally relevant (but maybe?)...
I'm happy to continue digging in, but you might be able to diagnose it much faster!
When digging I noticed that here https://github.com/mswjs/interceptors/blob/133754688adeb47cb972ab358db5e77f30084e03/src/interceptors/XMLHttpRequest/XMLHttpRequestController.ts#L335 there is never a .body
in the response, but there is a ._bodyInit
and ._bodyText
... not sure if that's helpful/important.
Let me know if you want a separate issue created or anything.
Yeah, React Native doesn't natively support a .body
on the Response
object, so the interceptor will always fail. Trying to think of a solution, open to ideas!
React Native also doesn't natively support ReadableStream
out of the box yet either I don't think....
I've been looking at this over here: https://github.com/mswjs/msw/issues/2085 and have narrowed something similar down to a repro.
When you run msw/axios in an environment that uses the node >= 18 APIs for Request/Response, everything works. But when running in an environment that gets those APIs from elsewhere - specifically whatwg-fetch, which is common in react projects in the testing environment, not sure about react-native but I did notice the dependency in @zibs repo - then it's borked.
This is not an issue with MSW, it's an issue with whatwg-fetch - I think this one - which might make it an issue to be raised over there (although the one I've referenced is closed), or perhaps an issue could be raised in react-native to use a different polyfill.
EDIT: Also same issue in RN repo. Hey, at least that one's open! EDIT 2: Possible solution SO answer
First of all, thank you all very much for your answers, I could not follow the issues for a while. I used an alternative library as a solution, thank you.
Handler:
http.get('*', ({ request }) => {
return HttpResponse.json({
data: { bebe: 1 },
});
})
LOG 13:40:52:219 [xhr:GET https://someurl.com] open GET https://someurl.com
LOG 13:40:52:228 [xhr:GET https://someurl.com] registered event "timeout" function handleTimeout() { [bytecode] }
LOG 13:40:52:233 [xhr:GET https://someurl.com] addEventListener timeout function handleTimeout() { [bytecode] }
LOG 13:40:52:242 [xhr:GET https://someurl.com] setRequestHeader Accept application/json, text/plain, */*
LOG 13:40:52:249 [xhr:GET https://someurl.com] registered event "load" function () { [bytecode] }
LOG 13:40:52:258 [xhr:GET https://someurl.com] addEventListener load function () { [bytecode] }
LOG 13:40:52:267 [xhr:GET https://someurl.com] converting request to a Fetch API Request...
LOG 13:40:52:275 [xhr:GET https://someurl.com] converted request to a Fetch API Request! {"url":"https://someurl.com","credentials":"include","headers":{"map":{"accept":"application/json, text/plain, */*"}},"method":"GET","mode":null,"signal":{},"referrer":null,"bodyUsed":false,"_bodyInit":null,"_noBody":true,"_bodyText":""}
LOG 13:40:52:284 [xhr:GET https://someurl.com] awaiting mocked response...
LOG 13:40:52:293 [xhr:GET https://someurl.com] emitting the "request" event for 2 listener(s)...
LOG 13:40:54:483 [xhr:GET https://someurl.com] all "request" listeners settled!
LOG 13:40:54:495 [xhr:GET https://someurl.com] event.respondWith called with: {"type":"default","status":200,"ok":true,"statusText":"OK","headers":{"map":{"content-type":"application/json","content-length":"19"}},"url":"","bodyUsed":false,"_bodyInit":"{\"data\":{\"bebe\":1}}","_bodyText":"{\"data\":{\"bebe\":1}}"}
LOG 13:40:54:503 [xhr:GET https://someurl.com] received mocked response: 200 OK
LOG 13:40:54:513 [xhr:GET https://someurl.com] responding with a mocked response: 200 OK
LOG 13:40:54:519 [xhr:GET https://someurl.com] calculated response body length 19
LOG 13:40:54:528 [xhr:GET https://someurl.com] trigger "loadstart" {"loaded":0,"total":19}
LOG 13:40:54:535 [xhr:GET https://someurl.com] setReadyState: 1 -> 2
LOG 13:40:54:542 [xhr:GET https://someurl.com] set readyState to: 2
LOG 13:40:54:548 [xhr:GET https://someurl.com] triggerring "readystatechange" event...
LOG 13:40:54:554 [xhr:GET https://someurl.com] trigger "readystatechange"
LOG 13:40:54:560 [xhr:GET https://someurl.com] setReadyState: 2 -> 3
LOG 13:40:54:566 [xhr:GET https://someurl.com] set readyState to: 3
LOG 13:40:54:573 [xhr:GET https://someurl.com] triggerring "readystatechange" event...
LOG 13:40:54:581 [xhr:GET https://someurl.com] trigger "readystatechange"
LOG 13:40:54:593 [xhr:GET https://someurl.com] finalizing the mocked response...
LOG 13:40:54:603 [xhr:GET https://someurl.com] setReadyState: 3 -> 4
LOG 13:40:54:609 [xhr:GET https://someurl.com] set readyState to: 4
LOG 13:40:54:618 [xhr:GET https://someurl.com] triggerring "readystatechange" event...
LOG 13:40:54:627 [xhr:GET https://someurl.com] trigger "readystatechange"
LOG 13:40:54:634 [xhr:GET https://someurl.com] trigger "load" {"loaded":0,"total":19}
LOG 13:40:54:643 [xhr:GET https://someurl.com] found 1 listener(s) for "load" event, calling...
LOG 13:40:54:651 [xhr:GET https://someurl.com] getResponse (responseType: )
LOG 13:40:54:659 [xhr:GET https://someurl.com] resolving "" response type as text
LOG 13:40:54:666 [xhr:GET https://someurl.com] getAllResponseHeaders
LOG 13:40:54:675 [xhr:GET https://someurl.com] resolved all response headers to content-type: application/json
content-length: 19
LOG 13:40:54:684 [xhr:GET https://someurl.com] emitting the "response" event for 1 listener(s)...
LOG 13:40:54:698 [xhr:GET https://someurl.com] trigger "loadend" {"loaded":0,"total":19}
LOG 13:40:54:708 [xhr:GET https://someurl.com] found a direct "loadend" callback, calling...
LOG 13:40:54:719 [xhr:GET https://someurl.com] getAllResponseHeaders
LOG 13:40:54:726 [xhr:GET https://someurl.com] resolved all response headers to content-type: application/json
content-length: 19
LOG 13:40:54:734 [xhr:GET https://someurl.com] getResponseText: "
I've found workaround. We can use _bodyInit
if we have no body.
diff --git a/lib/browser/chunk-65PS3XCB.js b/lib/browser/chunk-65PS3XCB.js
index e4f824e7d40d3d2a48c86b2420cbfd8e7c2c53ed..07228344b25fb6b44bb396f4b9eb93332d11cac2 100644
--- a/lib/browser/chunk-65PS3XCB.js
+++ b/lib/browser/chunk-65PS3XCB.js
@@ -455,6 +455,13 @@ var XMLHttpRequestController = class {
readNextResponseBodyChunk();
};
readNextResponseBodyChunk();
+ } else if (response._bodyInit) {
+ this.logger.info('mocked response has _bodyInit, faking streaming...')
+
+ const bodyInit = response._bodyInit
+ const encoder = new TextEncoder()
+ this.responseBuffer = encoder.encode(bodyInit)
+ finalizeResponse()
} else {
finalizeResponse();
}
Here is fixed logs:
LOG 14:56:48:940 [xhr:GET https://someurl.com] received mocked response: 200 OK
LOG 14:56:48:964 [xhr:GET https://someurl.com] responding with a mocked response: 200 OK
LOG 14:56:48:982 [xhr:GET https://someurl.com] calculated response body length 19
LOG 14:56:49:11 [xhr:GET https://someurl.com] trigger "loadstart" {"loaded":0,"total":19}
LOG 14:56:49:37 [xhr:GET https://someurl.com] setReadyState: 1 -> 2
LOG 14:56:49:59 [xhr:GET https://someurl.com] set readyState to: 2
LOG 14:56:49:99 [xhr:GET https://someurl.com] triggerring "readystatechange" event...
LOG 14:56:49:139 [xhr:GET https://someurl.com] trigger "readystatechange"
LOG 14:56:49:180 [xhr:GET https://someurl.com] setReadyState: 2 -> 3
LOG 14:56:49:223 [xhr:GET https://someurl.com] set readyState to: 3
LOG 14:56:49:262 [xhr:GET https://someurl.com] triggerring "readystatechange" event...
LOG 14:56:49:298 [xhr:GET https://someurl.com] trigger "readystatechange"
LOG 14:56:49:340 [xhr:GET https://someurl.com] mocked response has _bodyInit, faking streaming...
LOG 14:56:49:387 [xhr:GET https://someurl.com] finalizing the mocked response...
LOG 14:56:49:433 [xhr:GET https://someurl.com] setReadyState: 3 -> 4
LOG 14:56:49:476 [xhr:GET https://someurl.com] set readyState to: 4
LOG 14:56:49:521 [xhr:GET https://someurl.com] triggerring "readystatechange" event...
LOG 14:56:49:566 [xhr:GET https://someurl.com] trigger "readystatechange"
LOG 14:56:49:608 [xhr:GET https://someurl.com] trigger "load" {"loaded":19,"total":19}
LOG 14:56:49:648 [xhr:GET https://someurl.com] found 1 listener(s) for "load" event, calling...
LOG 14:56:49:692 [xhr:GET https://someurl.com] getResponse (responseType: )
LOG 14:56:49:743 [xhr:GET https://someurl.com] resolving "" response type as text {"data":{"bebe":1}}
LOG 14:56:49:785 [xhr:GET https://someurl.com] getAllResponseHeaders
LOG 14:56:49:837 [xhr:GET https://someurl.com] resolved all response headers to content-type: application/json
content-length: 19
LOG 14:56:49:887 [xhr:GET https://someurl.com] emitting the "response" event for 1 listener(s)...
LOG 14:56:49:933 [xhr:GET https://someurl.com] trigger "loadend" {"loaded":19,"total":19}
LOG 14:56:49:976 [xhr:GET https://someurl.com] found a direct "loadend" callback, calling...
LOG 14:56:50:32 [xhr:GET https://someurl.com] getAllResponseHeaders
LOG 14:56:50:71 [xhr:GET https://someurl.com] resolved all response headers to content-type: application/json
content-length: 19
LOG 14:56:50:118 [xhr:GET https://someurl.com] getResponseText: "{"data":{"bebe":1}}"
@kettanaito Can this workaround to be useful inside interceptors package?
Any fix planned ?
@hardouinyann you can use my patch for now
I've found workaround. We can use
_bodyInit
if we have no body.diff --git a/lib/browser/chunk-65PS3XCB.js b/lib/browser/chunk-65PS3XCB.js index e4f824e7d40d3d2a48c86b2420cbfd8e7c2c53ed..07228344b25fb6b44bb396f4b9eb93332d11cac2 100644 --- a/lib/browser/chunk-65PS3XCB.js +++ b/lib/browser/chunk-65PS3XCB.js @@ -455,6 +455,13 @@ var XMLHttpRequestController = class { readNextResponseBodyChunk(); }; readNextResponseBodyChunk(); + } else if (response._bodyInit) { + this.logger.info('mocked response has _bodyInit, faking streaming...') + + const bodyInit = response._bodyInit + const encoder = new TextEncoder() + this.responseBuffer = encoder.encode(bodyInit) + finalizeResponse() } else { finalizeResponse(); }
For anybody who comes across this, this patch needs to be applied on the @mswjs/interceptors
-package, NOT msw
Yep that's true
It's working! I hope it will merge asap! thank you! @XantreDev
It's working! I hope it will merge asap! thank you! @XantreDev
I would like to provide a PR if @kettanaito consider it usefull
@kettanaito is this fix something you'd consider merging? (asking because this issue is closed at the moment)
I'd rather only apply a patch as a stopgap for a proper fix, otherwise MSW just isn't an option for us for now :(
@kettanaito, any update on this?