I have an express server that makes calls to third party apis, and I would like to test my express server using supertest and mock the third party api requests using msw.
When starting the msw node server, all supertest requests fail with the following error:
TypeError [ERR_INVALID_URL]: Invalid URL: undefined//undefined
at onParseError (internal/url.js:241:17)
at new URL (internal/url.js:319:5)
at new URL (internal/url.js:316:22)
at normalizeHttpRequestParams (node_modules/node-request-interceptor/lib/http/ClientRequest/normalizeHttpRequestParams.js:29:57)
at new ClientRequestOverride (node_modules/node-request-interceptor/lib/http/ClientRequest/ClientRequestOverride.js:65:74)
at handleRequest (node_modules/node-request-interceptor/lib/http/override.js:28:12)
at Object.requestOverride [as request] (node_modules/node-request-interceptor/lib/http/override.js:55:20)
at Test.Request.request (node_modules/superagent/lib/node/index.js:622:31)
at Test.Request.end (node_modules/superagent/lib/node/index.js:764:8)
at Test.end (node_modules/supertest/lib/test.js:125:7)
msw: 0.19.0nodejs: 10.16.3npm: 6.9.0Steps to reproduce the behavior:
import express from 'express';
import request from 'supertest';
import { setupServer } from 'msw/node';
const app = express();
app.get('/', (req, res) => res.send('hello world'));
setupServer().listen();
request(app)
.get('/')
.then((res) => {
console.log(res);
});
Hey, @boblauer. Thanks for reaching out.
I can see that, although you're calling setupServer(), you are not passing any request handlers to that function. The way MSW works is that you pass what is called a request handler as arguments to the setupServer/setupWorker functions.
For example, if you wish to intercept a / request to your Node app, you should do:
import { rest } from 'msw'
import { setupServer } from 'msw'
setupServer(
// The `rest.get` call is a request handler
rest.get('http://localhost:8080/', (req, res, ctx) => {
return res(ctx.json({ intercepted: 'sure' }))
}
).listen()
Notice how I'm giving an absolute URL to rest.get(). That is because there cannot be any relative URLs in Node (nothing to be relative to).
If you're wondering how come
app.get('/')works with a relative URL, it's actually not relative either. It's just in Express you establish a server on a specific address later on (viaapp.listen(), so Express router knows what are those routes are relative to).
MSW is agnostic of your server framework, so if you wish it to intercept a request to your app, provide an _absolute URL_ that you wish to intercept. In the example above I've assumed that the app is established on localhost at the port 8080. Those values may be different in your case, so make sure to adjust them.
Also, from your original task I got the impression that you'd like to mock a third-party request, not the request to your app. Perhaps, you should consider:
setupServer(
// Intercepting a third-party request
rest.get('https://third-party.address/route', (req, res, ctx) => {...})
).listen()
// Request to a third-party server
request('https://third-party.address/route')
Let me know if that helps.
Thanks for the response. My example was more simplistic than my real world code.
In my real world code, I was intercepting the third-party request made from _within_ my express controller, but what I don't want to intercept is the call to my express controller. So my code looked a lot like your last example, except my request to a third-party server is happening with an express controller.
However, regardless of what I pass to setupServer, when supertest tries to call my express controller, I get the error mentioned in the original post. I could try intercepting the call to localhost to see if that works, although as you know that's not really what I'm trying to achieve.
I've set up a reproduction repository and was able to get the same issue.
Some of the Socket methods are missing on the Socket instance that is established by node-request-interceptor. I will tackle the issue there and propagate the fix to MSW.
The issue is caused by the RequestOptions object supertest creates. Unlike regular NodeJS RequestOptions, the one from the mentioned library doesn't have the protocol and hostname property, which resulted into the derived url being undefined//undefined.
I'm going to provide a fix in the node-request-interceptor library (responsible for requests interception and mocking in Node) and propagate it to MSW.
The fix is published in 0.19.2. Could you please update and let me know if it works for you? Thanks.
I am having this issue in version 0.19.5 while trying to test my GraphQL app. The GraphQL resolver within the app makes an external API call to a 3rd party to get a list of websites. The mocking of that call using msw is working properly. But, when I combine it with supertest in my testing, I am getting a 500 response along with the undefined undefined error.
Here is my test file:
import supertest from 'supertest';
import { rest } from 'msw';
import { setupServer } from 'msw/node';
// app is my ApolloServer
import { app } from '../app';
import { getWebsites } from './gql';
const server = setupServer(
rest.get(`https://the-third-party-api.com/created`, (_req, res, ctx) => {
return res(ctx.json([`fake-site-1`, `fake-site-2`]));
})
);
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
test(`Get all websites`, async () => {
await supertest(app)
.post(`/`)
.send({ query: getWebsites })
.then(({ error }) => {
console.log(error);
expect(error).toBeFalsy();
});
});
Here is the output when I run the test:
expect(received).toBeFalsy()
Received: [Error: cannot undefined undefined (500)]
console.log
Error: cannot undefined undefined (500)
at Response.toError (node_modules/superagent/lib/node/response.js:94:15)
at ResponseBase._setStatusProperties (node_modules/superagent/lib/response-base.js:123:16)
at new Response (node_modules/superagent/lib/node/response.js:41:8)
at Test.Request._emitResponse (node_modules/superagent/lib/node/index.js:752:20)
at IncomingMessage.<anonymous> (node_modules/superagent/lib/node/index.js:916:38)
at IncomingMessage.emit (events.js:326:22)
at endReadableNT (_stream_readable.js:1226:12)
at processTicksAndRejections (internal/process/task_queues.js:80:21) {
status: 500,
text: 'POST body missing. Did you forget use body-parser middleware?',
method: undefined,
path: undefined
}
If I remove the msw setupServer code, then the test passes.
Am I doing something wrong?
Hey, @bopfer. Sorry to hear that. Could you please let me know the exact versions of these dependencies that you are using?
$ npm ls msw
$ npm ls node-request-interceptor
The packages seem to be up to date, thanks.
One thing I can notice is that you mock a rest.get() request, while in supertest you perform a .post() request. Could you please align those?
The post is to my GraphQL app via supertest, which I do not want mocked. In there, the resolver code does a get to the 3rd party API. That's the part I would like mocked.
Something else must be at place then. Could you please set up a minimum reproduction repository for us to look? That'd be most useful.
Will do! This is sort of a side project. So, it may take me a few days to put something together.
Here is the reproduction repo: https://github.com/bopfer/msw-supertest-issues
I tried to make it as slim as possible to show the issue.
@kettanaito is there a fix for this issue and even i am getting the same problem.
@manishiitg, hey. The fix for _this_ issue has been provided. The issue you are experiencing may be similar, but not the same. Thanks for opening an issue regarding your problem, I'll look into that.
@bopfer, thanks for making a reproduction repository, this is most useful!
I confirm that error you mention in the README is thrown during yarn test. Do you mind creating a separate issue for this?
I understand some of the issue also relate to supertest, but let's treat it this way: if there's a closed ticket then it most likely means it's been resolved. The issue you are experiencing may be similar (even identical), but technically may have a completely different reason. I'd appreciate if those issues are filed separately, that would make them easier to navigate for future generations. Thanks.
Most helpful comment
Technical details
The issue is caused by the
RequestOptionsobjectsupertestcreates. Unlike regular NodeJSRequestOptions, the one from the mentioned library doesn't have theprotocolandhostnameproperty, which resulted into the derived url beingundefined//undefined.I'm going to provide a fix in the
node-request-interceptorlibrary (responsible for requests interception and mocking in Node) and propagate it to MSW.