HTTP server mocking and expectations library for Node.js

Overview

Nock

npm Build Status Coverage Status Dependabot Status Backers on Open Collective Sponsors on Open Collective

HTTP server mocking and expectations library for Node.js

Nock can be used to test modules that perform HTTP requests in isolation.

For instance, if a module performs HTTP requests to a CouchDB server or makes HTTP requests to the Amazon API, you can test that module in isolation.

Table of Contents

How does it work?

Nock works by overriding Node's http.request function. Also, it overrides http.ClientRequest too to cover for modules that use it directly.

Install

$ npm install --save-dev nock

Node version support

The latest version of nock supports all currently maintained Node versions, see Node Release Schedule

Here is a list of past nock versions with respective node version support

node nock
0.10 up to 8.x
0.11 up to 8.x
0.12 up to 8.x
4 up to 9.x
5 up to 8.x
6 up to 10.x
7 up to 9.x
8 up to 11.x
9 up to 9.x

Usage

On your test, you can setup your mocking object like this:

const nock = require('nock')

const scope = nock('https://api.github.com')
  .get('/repos/atom/atom/license')
  .reply(200, {
    license: {
      key: 'mit',
      name: 'MIT License',
      spdx_id: 'MIT',
      url: 'https://api.github.com/licenses/mit',
      node_id: 'MDc6TGljZW5zZTEz',
    },
  })

This setup says that we will intercept every HTTP call to https://api.github.com.

It will intercept an HTTPS GET request to /repos/atom/atom/license, reply with a status 200, and the body will contain a (partial) response in JSON.

READ THIS! - About interceptors

When you setup an interceptor for a URL and that interceptor is used, it is removed from the interceptor list. This means that you can intercept 2 or more calls to the same URL and return different things on each of them. It also means that you must setup one interceptor for each request you are going to have, otherwise nock will throw an error because that URL was not present in the interceptor list. If you don’t want interceptors to be removed as they are used, you can use the .persist() method.

Specifying hostname

The request hostname can be a string or a RegExp.

const scope = nock('http://www.example.com')
  .get('/resource')
  .reply(200, 'domain matched')
const scope = nock(/example\.com/)
  .get('/resource')
  .reply(200, 'domain regex matched')

Note: You can choose to include or not the protocol in the hostname matching.

Specifying path

The request path can be a string, a RegExp or a filter function and you can use any HTTP verb.

Using a string:

const scope = nock('http://www.example.com')
  .get('/resource')
  .reply(200, 'path matched')

Using a regular expression:

const scope = nock('http://www.example.com')
  .get(/source$/)
  .reply(200, 'path using regex matched')

Using a function:

const scope = nock('http://www.example.com')
  .get(uri => uri.includes('cats'))
  .reply(200, 'path using function matched')

Specifying request body

You can specify the request body to be matched as the second argument to the get, post, put or delete specifications. There are five types of second argument allowed:

String: nock will exact match the stringified request body with the provided string

nock('http://www.example.com')
  .post('/login', 'username=pgte&password=123456')
  .reply(200, { id: '123ABC' })

Buffer: nock will exact match the stringified request body with the provided buffer

nock('http://www.example.com')
  .post('/login', Buffer.from([0xff, 0x11]))
  .reply(200, { id: '123ABC' })

RegExp: nock will test the stringified request body against the provided RegExp

nock('http://www.example.com')
  .post('/login', /username=\w+/gi)
  .reply(200, { id: '123ABC' })

JSON object: nock will exact match the request body with the provided object. In order to increase flexibility, nock also supports RegExp as an attribute value for the keys:

nock('http://www.example.com')
  .post('/login', { username: 'pgte', password: /.+/i })
  .reply(200, { id: '123ABC' })

Function: nock will evaluate the function providing the request body object as first argument. Return true if it should be considered a match:

nock('http://www.example.com')
  .post('/login', body => body.username && body.password)
  .reply(200, { id: '123ABC' })

In case you need to perform a partial matching on a complex, nested request body you should have a look at libraries like lodash.matches. Indeed, partial matching can be achieved as:

nock('http://www.example.com')
  .post('/user', _.matches({ address: { country: 'US' } }))
  .reply(200, { id: '123ABC' })

Specifying request query string

Nock understands query strings. Search parameters can be included as part of the path:

nock('http://example.com').get('/users?foo=bar').reply(200)

Instead of placing the entire URL, you can specify the query part as an object:

nock('http://example.com')
  .get('/users')
  .query({ name: 'pedro', surname: 'teixeira' })
  .reply(200, { results: [{ id: 'pgte' }] })

Nock supports array-style/object-style query parameters. The encoding format matches with request module.

nock('http://example.com')
  .get('/users')
  .query({
    names: ['alice', 'bob'],
    tags: {
      alice: ['admin', 'tester'],
      bob: ['tester'],
    },
  })
  .reply(200, { results: [{ id: 'pgte' }] })

A URLSearchParams instance can be provided.

const params = new URLSearchParams({ foo: 'bar' })

nock('http://example.com').get('/').query(params).reply(200)

Nock supports passing a function to query. The function determines if the actual query matches or not.

nock('http://example.com')
  .get('/users')
  .query(actualQueryObject => {
    // do some compare with the actual Query Object
    // return true for matched
    // return false for not matched
    return true
  })
  .reply(200, { results: [{ id: 'pgte' }] })

To mock the entire url regardless of the passed query string:

nock('http://example.com')
  .get('/users')
  .query(true)
  .reply(200, { results: [{ id: 'pgte' }] })

A query string that is already URL encoded can be matched by passing the encodedQueryParams flag in the options when creating the Scope.

nock('http://example.com', { encodedQueryParams: true })
  .get('/users')
  .query('foo%5Bbar%5D%3Dhello%20world%21')
  .reply(200, { results: [{ id: 'pgte' }] })

Specifying replies

You can specify the return status code for a path on the first argument of reply like this:

const scope = nock('http://myapp.iriscouch.com').get('/users/1').reply(404)

You can also specify the reply body as a string:

const scope = nock('http://www.google.com')
  .get('/')
  .reply(200, 'Hello from Google!')

or as a JSON-encoded object:

const scope = nock('http://myapp.iriscouch.com').get('/').reply(200, {
  username: 'pgte',
  email: '[email protected]',
  _id: '4324243fsd',
})

or even as a file:

const scope = nock('http://myapp.iriscouch.com')
  .get('/')
  .replyWithFile(200, __dirname + '/replies/user.json', {
    'Content-Type': 'application/json',
  })

Instead of an object or a buffer you can also pass in a callback to be evaluated for the value of the response body:

const scope = nock('http://www.google.com')
  .post('/echo')
  .reply(201, (uri, requestBody) => requestBody)

In Nock 11.x it was possible to invoke .reply() with a status code and a function that returns an array containing a status code and body. (The status code from the array would take precedence over the one passed directly to reply.) This is no longer allowed. In Nock 12 and later, either call .reply() with a status code and a function that returns the body, or call it with a single argument: a function that returns an array containing both the status code and body.

An asynchronous function that gets an error-first callback as its last argument also works:

const scope = nock('http://www.google.com')
  .post('/echo')
  .reply(201, (uri, requestBody, cb) => {
    fs.readFile('cat-poems.txt', cb) // Error-first callback
  })

In Nock 11 and later, if an error is passed to the callback, Nock will rethrow it as a programmer error. In Nock 10 and earlier, the error was sent in the response body, with a 500 HTTP response status code.

You can also return the status code and body using just one function:

const scope = nock('http://www.google.com')
  .post('/echo')
  .reply((uri, requestBody) => {
    return [
      201,
      'THIS IS THE REPLY BODY',
      { header: 'value' }, // optional headers
    ]
  })

or, use an error-first callback that also gets the status code:

const scope = nock('http://www.google.com')
  .post('/echo')
  .reply((uri, requestBody, cb) => {
    setTimeout(() => cb(null, [201, 'THIS IS THE REPLY BODY']), 1000)
  })

A Stream works too:

const scope = nock('http://www.google.com')
  .get('/cat-poems')
  .reply(200, (uri, requestBody) => {
    return fs.createReadStream('cat-poems.txt')
  })

Access original request and headers

If you're using the reply callback style, you can access the original client request using this.req like this:

const scope = nock('http://www.google.com')
  .get('/cat-poems')
  .reply(function (uri, requestBody) {
    console.log('path:', this.req.path)
    console.log('headers:', this.req.headers)
    // ...
  })

Note: Remember to use normal function in that case, as arrow functions are using enclosing scope for this binding.

Replying with errors

You can reply with an error like this:

nock('http://www.google.com')
  .get('/cat-poems')
  .replyWithError('something awful happened')

JSON error responses are allowed too:

nock('http://www.google.com').get('/cat-poems').replyWithError({
  message: 'something awful happened',
  code: 'AWFUL_ERROR',
})

Note: This will emit an error event on the request object, not the reply.

Specifying headers

Header field names are case-insensitive

Per HTTP/1.1 4.2 Message Headers specification, all message headers are case insensitive and thus internally Nock uses lower-case for all field names even if some other combination of cases was specified either in mocking specification or in mocked requests themselves.

Specifying Request Headers

You can specify the request headers like this:

const scope = nock('http://www.example.com', {
  reqheaders: {
    authorization: 'Basic Auth',
  },
})
  .get('/')
  .reply(200)

Or you can use a regular expression or function to check the header values. The function will be passed the header value.

const scope = nock('http://www.example.com', {
  reqheaders: {
    'X-My-Headers': headerValue => headerValue.includes('cats'),
    'X-My-Awesome-Header': /Awesome/i,
  },
})
  .get('/')
  .reply(200)

If reqheaders is not specified or if host is not part of it, Nock will automatically add host value to request header.

If no request headers are specified for mocking then Nock will automatically skip matching of request headers. Since the host header is a special case which may get automatically inserted by Nock, its matching is skipped unless it was also specified in the request being mocked.

You can also have Nock fail the request if certain headers are present:

const scope = nock('http://www.example.com', {
  badheaders: ['cookie', 'x-forwarded-for'],
})
  .get('/')
  .reply(200)

When invoked with this option, Nock will not match the request if any of the badheaders are present.

Basic authentication can be specified as follows:

const scope = nock('http://www.example.com')
  .get('/')
  .basicAuth({ user: 'john', pass: 'doe' })
  .reply(200)

Specifying Reply Headers

You can specify the reply headers like this:

const scope = nock('https://api.github.com')
  .get('/repos/atom/atom/license')
  .reply(200, { license: 'MIT' }, { 'X-RateLimit-Remaining': 4999 })

Or you can use a function to generate the headers values. The function will be passed the request, response, and response body (if available). The body will be either a buffer, a stream, or undefined.

const scope = nock('http://www.headdy.com')
  .get('/')
  .reply(200, 'Hello World!', {
    'Content-Length': (req, res, body) => body.length,
    ETag: () => `${Date.now()}`,
  })

Default Reply Headers

You can also specify default reply headers for all responses like this:

const scope = nock('http://www.headdy.com')
  .defaultReplyHeaders({
    'X-Powered-By': 'Rails',
    'Content-Type': 'application/json',
  })
  .get('/')
  .reply(200, 'The default headers should come too')

Or you can use a function to generate the default headers values:

const scope = nock('http://www.headdy.com')
  .defaultReplyHeaders({
    'Content-Length': (req, res, body) => body.length,
  })
  .get('/')
  .reply(200, 'The default headers should come too')

Including Content-Length Header Automatically

When using interceptor.reply() to set a response body manually, you can have the Content-Length header calculated automatically.

const scope = nock('http://www.headdy.com')
  .replyContentLength()
  .get('/')
  .reply(200, { hello: 'world' })

NOTE: this does not work with streams or other advanced means of specifying the reply body.

Including Date Header Automatically

You can automatically append a Date header to your mock reply:

const scope = nock('http://www.headdy.com')
  .replyDate()
  .get('/')
  .reply(200, { hello: 'world' })

Or provide your own Date object:

const scope = nock('http://www.headdy.com')
  .replyDate(new Date(2015, 0, 1))
  .get('/')
  .reply(200, { hello: 'world' })

HTTP Verbs

Nock supports any HTTP verb, and it has convenience methods for the GET, POST, PUT, HEAD, DELETE, PATCH, OPTIONS and MERGE HTTP verbs.

You can intercept any HTTP verb using .intercept(path, verb [, requestBody [, options]]):

const scope = nock('http://my.domain.com')
  .intercept('/path', 'PATCH')
  .reply(304)

Support for HTTP and HTTPS

By default nock assumes HTTP. If you need to use HTTPS you can specify the https:// prefix like this:

const scope = nock('https://secure.my.server.com')
// ...

Non-standard ports

You are able to specify a non-standard port like this:

const scope = nock('http://my.server.com:8081')

Repeat response n times

You are able to specify the number of times to repeat the same response.

NOTE: When request times is more than the number you specified, you will get an error before cleaning this interceptor.

nock('http://zombo.com').get('/').times(4).reply(200, 'Ok')

http.get('http://zombo.com/') // respond body "Ok"
http.get('http://zombo.com/') // respond body "Ok"
http.get('http://zombo.com/') // respond body "Ok"
http.get('http://zombo.com/') // respond body "Ok"

// This code will get an error with message:
// Nock: No match for request
http.get('http://zombo.com/')

// clean your interceptor
nock.cleanAll()

http.get('http://zombo.com/') // real respond with zombo.com result

Sugar syntax

nock('http://zombo.com').get('/').once().reply(200, 'Ok')
nock('http://zombo.com').get('/').twice().reply(200, 'Ok')
nock('http://zombo.com').get('/').thrice().reply(200, 'Ok')

To repeat this response for as long as nock is active, use .persist().

Delay the response

Nock can simulate response latency to allow you to test timeouts, race conditions, an other timing related scenarios.
You are able to specify the number of milliseconds that your reply should be delayed.

nock('http://my.server.com')
  .get('/')
  .delay(2000) // 2 seconds delay will be applied to the response header.
  .reply(200, '')

delay(1000) is an alias for delayConnection(1000).delayBody(0)
delay({ head: 1000, body: 2000 }) is an alias for delayConnection(1000).delayBody(2000)
Both of which are covered in detail below.

Delay the connection

You are able to specify the number of milliseconds that your connection should be idle before it starts to receive the response.

To simulate a socket timeout, provide a larger value than the timeout setting on the request.

nock('http://my.server.com')
  .get('/')
  .delayConnection(2000) // 2 seconds
  .reply(200, '')

req = http.request('http://my.server.com', { timeout: 1000 })

Nock emits timeout events almost immediately by comparing the requested connection delay to the timeout parameter passed to http.request() or http.ClientRequest#setTimeout().
This allows you to test timeouts without using fake timers or slowing down your tests. If the client chooses to not take an action (e.g. abort the request), the request and response will continue on as normal, after real clock time has passed.

Technical Details

Following the 'finish' event being emitted by ClientRequest, Nock will wait for the next event loop iteration before checking if the request has been aborted. At this point, any connection delay value is compared against any request timeout setting and a 'timeout' is emitted when appropriate from the socket and the request objects. A Node timeout timer is then registered with any connection delay value to delay real time before checking again if the request has been aborted and the 'response' is emitted by the request.

A similar method, .socketDelay() was removed in version 13. It was thought that having two methods so subtlety similar was confusing.
The discussion can be found at https://github.com/nock/nock/pull/1974.

Delay the response body

You are able to specify the number of milliseconds that the response body should be delayed.
This is the time between the headers being received and the body starting to be received.

nock('http://my.server.com')
  .get('/')
  .delayBody(2000) // 2 seconds
  .reply(200, '')
Technical Details

Following the 'response' being emitted by ClientRequest, Nock will register a timeout timer with the body delay value to delay real time before the IncomingMessage emits its first 'data' or the 'end' event.

Chaining

You can chain behaviour like this:

const scope = nock('http://myapp.iriscouch.com')
  .get('/users/1')
  .reply(404)
  .post('/users', {
    username: 'pgte',
    email: '[email protected]',
  })
  .reply(201, {
    ok: true,
    id: '123ABC',
    rev: '946B7D1C',
  })
  .get('/users/123ABC')
  .reply(200, {
    _id: '123ABC',
    _rev: '946B7D1C',
    username: 'pgte',
    email: '[email protected]',
  })

Scope filtering

You can filter the scope (protocol, domain or port) of nock through a function. The filtering function is accepted at the filteringScope field of the options argument.

This can be useful if you have a node module that randomly changes subdomains to which it sends requests, e.g., the Dropbox node module behaves like this.

const scope = nock('https://api.dropbox.com', {
  filteringScope: scope => /^https:\/\/api[0-9]*.dropbox.com/.test(scope),
})
  .get('/1/metadata/auto/Photos?include_deleted=false&list=true')
  .reply(200)

Conditional scope filtering

You can also choose to filter out a scope based on your system environment (or any external factor). The filtering function is accepted at the conditionally field of the options argument.

This can be useful if you only want certain scopes to apply depending on how your tests are executed.

const scope = nock('https://api.myservice.com', {
  conditionally: () => true,
})

Path filtering

You can also filter the URLs based on a function.

This can be useful, for instance, if you have random or time-dependent data in your URL.

You can use a regexp for replacement, just like String.prototype.replace:

const scope = nock('http://api.myservice.com')
  .filteringPath(/password=[^&]*/g, 'password=XXX')
  .get('/users/1?password=XXX')
  .reply(200, 'user')

Or you can use a function:

const scope = nock('http://api.myservice.com')
  .filteringPath(path => '/ABC')
  .get('/ABC')
  .reply(200, 'user')

Note that scope.filteringPath is not cumulative: it should only be used once per scope.

Request Body filtering

You can also filter the request body based on a function.

This can be useful, for instance, if you have random or time-dependent data in your request body.

You can use a regexp for replacement, just like String.prototype.replace:

const scope = nock('http://api.myservice.com')
  .filteringRequestBody(/password=[^&]*/g, 'password=XXX')
  .post('/users/1', 'data=ABC&password=XXX')
  .reply(201, 'OK')

Or you can use a function to transform the body:

const scope = nock('http://api.myservice.com')
  .filteringRequestBody(body => 'ABC')
  .post('/', 'ABC')
  .reply(201, 'OK')

If you don't want to match the request body you should omit the body argument from the method function:

const scope = nock('http://api.myservice.com')
  .post('/some_uri') // no body argument
  .reply(200, 'OK')

Request Headers Matching

If you need to match requests only if certain request headers match, you can.

const scope = nock('http://api.myservice.com')
  .matchHeader('accept', 'application/json')
  .get('/')
  .reply(200, {
    data: 'hello world',
  })

You can also use a regexp for the header body.

const scope = nock('http://api.myservice.com')
  .matchHeader('User-Agent', /Mozilla\/.*/)
  .get('/')
  .reply(200, {
    data: 'hello world',
  })

You can also use a function for the header body.

const scope = nock('http://api.myservice.com')
  .matchHeader('content-length', val => val >= 1000)
  .get('/')
  .reply(200, {
    data: 'hello world',
  })

Optional Requests

By default every mocked request is expected to be made exactly once, and until it is it'll appear in scope.pendingMocks(), and scope.isDone() will return false (see expectations). In many cases this is fine, but in some (especially cross-test setup code) it's useful to be able to mock a request that may or may not happen. You can do this with optionally(). Optional requests are consumed just like normal ones once matched, but they do not appear in pendingMocks(), and isDone() will return true for scopes with only optional requests pending.

const example = nock('http://example.com')
example.pendingMocks() // []
example.get('/pathA').reply(200)
example.pendingMocks() // ["GET http://example.com:80/path"]

// ...After a request to example.com/pathA:
example.pendingMocks() // []

example.get('/pathB').optionally().reply(200)
example.pendingMocks() // []

// You can also pass a boolean argument to `optionally()`. This
// is useful if you want to conditionally make a mocked request
// optional.
const getMock = optional =>
  example.get('/pathC').optionally(optional).reply(200)

getMock(true)
example.pendingMocks() // []
getMock(false)
example.pendingMocks() // ["GET http://example.com:80/pathC"]

Allow unmocked requests on a mocked hostname

If you need some request on the same host name to be mocked and some others to really go through the HTTP stack, you can use the allowUnmocked option like this:

const scope = nock('http://my.existing.service.com', { allowUnmocked: true })
  .get('/my/url')
  .reply(200, 'OK!')

// GET /my/url => goes through nock
// GET /other/url => actually makes request to the server

Note: When applying {allowUnmocked: true}, if the request is made to the real server, no interceptor is removed.

Expectations

Every time an HTTP request is performed for a scope that is mocked, Nock expects to find a handler for it. If it doesn't, it will throw an error.

Calls to nock() return a scope which you can assert by calling scope.done(). This will assert that all specified calls on that scope were performed.

Example:

const scope = nock('http://google.com')
  .get('/')
  .reply(200, 'Hello from Google!')

// do some stuff

setTimeout(() => {
  // Will throw an assertion error if meanwhile a "GET http://google.com" was
  // not performed.
  scope.done()
}, 5000)

.isDone()

You can call isDone() on a single expectation to determine if the expectation was met:

const scope = nock('http://google.com').get('/').reply(200)

scope.isDone() // will return false

It is also available in the global scope, which will determine if all expectations have been met:

nock.isDone()

.cleanAll()

You can cleanup all the prepared mocks (could be useful to cleanup some state after a failed test) like this:

nock.cleanAll()

.abortPendingRequests()

You can abort all current pending request like this:

nock.abortPendingRequests()

.persist()

You can make all the interceptors for a scope persist by calling .persist() on it:

const scope = nock('http://example.com')
  .persist()
  .get('/')
  .reply(200, 'Persisting all the way')

Note that while a persisted scope will always intercept the requests, it is considered "done" after the first interception.

If you want to stop persisting an individual persisted mock you can call persist(false):

const scope = nock('http://example.com').persist().get('/').reply(200, 'ok')

// Do some tests ...

scope.persist(false)

You can also use nock.cleanAll() which removes all mocks, including persistent mocks.

To specify an exact number of times that nock should repeat the response, use .times().

.pendingMocks()

If a scope is not done, you can inspect the scope to infer which ones are still pending using the scope.pendingMocks() function:

if (!scope.isDone()) {
  console.error('pending mocks: %j', scope.pendingMocks())
}

It is also available in the global scope:

console.error('pending mocks: %j', nock.pendingMocks())

.activeMocks()

You can see every mock that is currently active (i.e. might potentially reply to requests) in a scope using scope.activeMocks(). A mock is active if it is pending, optional but not yet completed, or persisted. Mocks that have intercepted their requests and are no longer doing anything are the only mocks which won't appear here.

You probably don't need to use this - it mainly exists as a mechanism to recreate the previous (now-changed) behavior of pendingMocks().

console.error('active mocks: %j', scope.activeMocks())

It is also available in the global scope:

console.error('active mocks: %j', nock.activeMocks())

.isActive()

Your tests may sometimes want to deactivate the nock interceptor. Once deactivated, nock needs to be re-activated to work. You can check if nock interceptor is active or not by using nock.isActive(). Sample:

if (!nock.isActive()) {
  nock.activate()
}

Restoring

You can restore the HTTP interceptor to the normal unmocked behaviour by calling:

nock.restore()

note 1: restore does not clear the interceptor list. Use nock.cleanAll() if you expect the interceptor list to be empty.

note 2: restore will also remove the http interceptor itself. You need to run nock.activate() to re-activate the http interceptor. Without re-activation, nock will not intercept any calls.

Activating

Only for cases where nock has been deactivated using nock.restore(), you can reactivate the HTTP interceptor to start intercepting HTTP calls using:

nock.activate()

note: To check if nock HTTP interceptor is active or inactive, use nock.isActive().

Turning Nock Off (experimental!)

You can bypass Nock completely by setting the NOCK_OFF environment variable to "true".

This way you can have your tests hit the real servers just by switching on this environment variable.

$ NOCK_OFF=true node my_test.js

Enable/Disable real HTTP requests

By default, any requests made to a host that is not mocked will be executed normally. If you want to block these requests, nock allows you to do so.

Disabling requests

For disabling real http requests.

nock.disableNetConnect()

So, if you try to request any host not 'nocked', it will throw a NetConnectNotAllowedError.

nock.disableNetConnect()
const req = http.get('http://google.com/')
req.on('error', err => {
  console.log(err)
})
// The returned `http.ClientRequest` will emit an error event (or throw if you're not listening for it)
// This code will log a NetConnectNotAllowedError with message:
// Nock: Disallowed net connect for "google.com:80"

Enabling requests

For enabling any real HTTP requests (the default behavior):

nock.enableNetConnect()

You could allow real HTTP requests for certain host names by providing a string or a regular expression for the hostname, or a function that accepts the hostname and returns true or false:

// Using a string
nock.enableNetConnect('amazon.com')

// Or a RegExp
nock.enableNetConnect(/(amazon|github)\.com/)

// Or a Function
nock.enableNetConnect(
  host => host.includes('amazon.com') || host.includes('github.com')
)

http.get('http://www.amazon.com/')
http.get('http://github.com/')

http.get('http://google.com/')
// This will throw NetConnectNotAllowedError with message:
// Nock: Disallowed net connect for "google.com:80"

A common use case when testing local endpoints would be to disable all but localhost, then add in additional nocks for external requests:

nock.disableNetConnect()
// Allow localhost connections so we can test local routes and mock servers.
nock.enableNetConnect('127.0.0.1')

Resetting NetConnect

When you're done with the test, you probably want to set everything back to normal:

nock.cleanAll()
nock.enableNetConnect()

Recording

This is a cool feature:

Guessing what the HTTP calls are is a mess, especially if you are introducing nock on your already-coded tests.

For these cases where you want to mock an existing live system you can record and playback the HTTP calls like this:

nock.recorder.rec()
// Some HTTP calls happen and the nock code necessary to mock
// those calls will be outputted to console

Recording relies on intercepting real requests and responses and then persisting them for later use.

In order to stop recording you should call nock.restore() and recording will stop.

ATTENTION!: when recording is enabled, nock does no validation, nor will any mocks be enabled. Please be sure to turn off recording before attempting to use any mocks in your tests.

dont_print option

If you just want to capture the generated code into a var as an array you can use:

nock.recorder.rec({
  dont_print: true,
})
// ... some HTTP calls
const nockCalls = nock.recorder.play()

The nockCalls var will contain an array of strings representing the generated code you need.

Copy and paste that code into your tests, customize at will, and you're done! You can call nock.recorder.clear() to remove already recorded calls from the array that nock.recorder.play() returns.

(Remember that you should do this one test at a time).

output_objects option

In case you want to generate the code yourself or use the test data in some other way, you can pass the output_objects option to rec:

nock.recorder.rec({
  output_objects: true,
})
// ... some HTTP calls
const nockCallObjects = nock.recorder.play()

The returned call objects have the following properties:

  • scope - the scope of the call including the protocol and non-standard ports (e.g. 'https://github.com:12345')
  • method - the HTTP verb of the call (e.g. 'GET')
  • path - the path of the call (e.g. '/pgte/nock')
  • body - the body of the call, if any
  • status - the HTTP status of the reply (e.g. 200)
  • response - the body of the reply which can be a JSON, string, hex string representing binary buffers or an array of such hex strings (when handling content-encoded in reply header)
  • headers - the headers of the reply
  • reqheader - the headers of the request

If you save this as a JSON file, you can load them directly through nock.load(path). Then you can post-process them before using them in the tests. For example, to add request body filtering (shown here fixing timestamps to match the ones captured during recording):

nocks = nock.load(pathToJson)
nocks.forEach(function (nock) {
  nock.filteringRequestBody = (body, aRecordedBody) => {
    if (typeof body !== 'string' || typeof aRecordedBody !== 'string') {
      return body
    }

    const recordedBodyResult = /timestamp:([0-9]+)/.exec(aRecordedBody)
    if (recordedBodyResult) {
      const recordedTimestamp = recordedBodyResult[1]
      return body.replace(
        /(timestamp):([0-9]+)/g,
        function (match, key, value) {
          return key + ':' + recordedTimestamp
        }
      )
    } else {
      return body
    }
  }
})

Alternatively, if you need to pre-process the captured nock definitions before using them (e.g. to add scope filtering) then you can use nock.loadDefs(path) and nock.define(nockDefs). Shown here is scope filtering for Dropbox node module which constantly changes the subdomain to which it sends the requests:

//  Pre-process the nock definitions as scope filtering has to be defined before the nocks are defined (due to its very hacky nature).
const nockDefs = nock.loadDefs(pathToJson)
nockDefs.forEach(def => {
  //  Do something with the definition object e.g. scope filtering.
  def.options = {
    ...def.options,
    filteringScope: scope => /^https:\/\/api[0-9]*.dropbox.com/.test(scope),
  }
})

//  Load the nocks from pre-processed definitions.
const nocks = nock.define(nockDefs)

enable_reqheaders_recording option

Recording request headers by default is deemed more trouble than it's worth as some of them depend on the timestamp or other values that may change after the tests have been recorded thus leading to complex postprocessing of recorded tests. Thus by default the request headers are not recorded.

The genuine use cases for recording request headers (e.g. checking authorization) can be handled manually or by using enable_reqheaders_recording in recorder.rec() options.

nock.recorder.rec({
  dont_print: true,
  output_objects: true,
  enable_reqheaders_recording: true,
})

Note that even when request headers recording is enabled Nock will never record user-agent headers. user-agent values change with the version of Node and underlying operating system and are thus useless for matching as all that they can indicate is that the user agent isn't the one that was used to record the tests.

logging option

Nock will print using console.log by default (assuming that dont_print is false). If a different function is passed into logging, nock will send the log string (or object, when using output_objects) to that function. Here's a basic example.

const appendLogToFile = content => {
  fs.appendFile('record.txt', content)
}
nock.recorder.rec({
  logging: appendLogToFile,
})

use_separator option

By default, nock will wrap its output with the separator string <<<<<<-- cut here -->>>>>> before and after anything it prints, whether to the console or a custom log function given with the logging option.

To disable this, set use_separator to false.

nock.recorder.rec({
  use_separator: false,
})

.removeInterceptor()

This allows removing a specific interceptor. This can be either an interceptor instance or options for a url. It's useful when there's a list of common interceptors shared between tests, where an individual test requires one of the shared interceptors to behave differently.

Examples:

nock.removeInterceptor({
  hostname: 'localhost',
  path: '/mockedResource',
})
nock.removeInterceptor({
  hostname: 'localhost',
  path: '/login',
  method: 'POST',
  proto: 'https',
})
const interceptor = nock('http://example.org').get('somePath')
nock.removeInterceptor(interceptor)

Events

A scope emits the following events:

  • emit('request', function(req, interceptor, body))
  • emit('replied', function(req, interceptor))

Global no match event

You can also listen for no match events like this:

nock.emitter.on('no match', req => {})

Nock Back

Fixture recording support and playback.

Setup

You must specify a fixture directory before using, for example:

In your test helper

const nockBack = require('nock').back

nockBack.fixtures = '/path/to/fixtures/'
nockBack.setMode('record')

Options

  • nockBack.fixtures : path to fixture directory
  • nockBack.setMode() : the mode to use

Usage

By default if the fixture doesn't exist, a nockBack will create a new fixture and save the recorded output for you. The next time you run the test, if the fixture exists, it will be loaded in.

The this context of the callback function will have a property scopes to access all of the loaded nock scopes.

const nockBack = require('nock').back
const request = require('request')
nockBack.setMode('record')

nockBack.fixtures = __dirname + '/nockFixtures' //this only needs to be set once in your test helper

// recording of the fixture
nockBack('zomboFixture.json', nockDone => {
  request.get('http://zombo.com', (err, res, body) => {
    nockDone()

    // usage of the created fixture
    nockBack('zomboFixture.json', function (nockDone) {
      http.get('http://zombo.com/').end() // respond body "Ok"

      this.assertScopesFinished() //throws an exception if all nocks in fixture were not satisfied
      http.get('http://zombo.com/').end() // throws exception because someFixture.json only had one call

      nockDone() //never gets here
    })
  })
})

If your tests are using promises then use nockBack like this:

return nockBack('promisedFixture.json').then(({ nockDone, context }) => {
  //  do your tests returning a promise and chain it with
  //  `.then(nockDone)`
})

Options

As an optional second parameter you can pass the following options

  • before: a preprocessing function, gets called before nock.define
  • after: a postprocessing function, gets called after nock.define
  • afterRecord: a postprocessing function, gets called after recording. Is passed the array of scopes recorded and should return the intact array, a modified version of the array, or if custom formatting is desired, a stringified version of the array to save to the fixture
  • recorder: custom options to pass to the recorder
Example
function prepareScope(scope) {
  scope.filteringRequestBody = (body, aRecordedBody) => {
    if (typeof body !== 'string' || typeof aRecordedBody !== 'string') {
      return body
    }

    const recordedBodyResult = /timestamp:([0-9]+)/.exec(aRecordedBody)
    if (recordedBodyResult) {
      const recordedTimestamp = recordedBodyResult[1]
      return body.replace(
        /(timestamp):([0-9]+)/g,
        (match, key, value) => `${key}:${recordedTimestamp}`
      )
    } else {
      return body
    }
  }
}

nockBack('exampleFixture.json', { before: prepareScope }, nockDone => {
  request.get('http://example.com', function (err, res, body) {
    // do your tests
    nockDone()
  })
})

Modes

To set the mode call nockBack.setMode(mode) or run the tests with the NOCK_BACK_MODE environment variable set before loading nock. If the mode needs to be changed programmatically, the following is valid: nockBack.setMode(nockBack.currentMode)

  • wild: all requests go out to the internet, don't replay anything, doesn't record anything

  • dryrun: The default, use recorded nocks, allow http calls, doesn't record anything, useful for writing new tests

  • record: use recorded nocks, record new nocks

  • lockdown: use recorded nocks, disables all http calls even when not nocked, doesn't record

Common issues

"No match for response" when using got with error responses

Got automatically retries failed requests twice. That means if you have a test which mocks a 4xx or 5xx response, got will immediately reissue it. At that point, the mock will have been consumed and the second request will error out with Nock: No match for request.

The same is true for .replyWithError().

Adding { retry: 0 } to the got invocations will disable retrying, e.g.:

await got('http://example.test/', { retry: 0 })

If you need to do this in all your tests, you can create a module got_client.js which exports a custom got instance:

const got = require('got')

module.exports = got.extend({ retry: 0 })

This is how it's handled in Nock itself (see #1523).

Axios

To use Nock with Axios, you may need to configure Axios to use the Node adapter as in the example below:

import axios from 'axios'
import nock from 'nock'
import test from 'ava' // You can use any test framework.

// If you are using jsdom, axios will default to using the XHR adapter which
// can't be intercepted by nock. So, configure axios to use the node adapter.
//
// References:
// https://github.com/nock/nock/issues/699#issuecomment-272708264
// https://github.com/axios/axios/issues/305
axios.defaults.adapter = require('axios/lib/adapters/http')

test('can fetch test response', async t => {
  // Set up the mock request.
  const scope = nock('http://localhost')
    .get('/test')
    .reply(200, 'test response')

  // Make the request. Note that the hostname must match exactly what is passed
  // to `nock()`. Alternatively you can set `axios.defaults.host = 'http://localhost'`
  // and run `axios.get('/test')`.
  await axios.get('http://localhost/test')

  // Assert that the expected request was made.
  scope.done()
})

Memory issues with Jest

Memory issues can be avoided by calling nock.restore() after each test suite.
One of the core principles of Jest is that it runs tests in isolation. It does this by manipulating the modules cache of Node in a way that conflicts with how Nock monkey patches the builtin http and https modules. Related issue with more details.

Debugging

Nock uses debug, so just run with environmental variable DEBUG set to nock.*.

user@local$ DEBUG=nock.* node my_test.js

Each step in the matching process is logged this way and can be useful when determining why a request was not intercepted by Nock.

For example the following shows that matching failed because the request had an extra search parameter.

nock('http://example.com').get('/').query({ foo: 'bar' }).reply()

await got('http://example.com/?foo=bar&baz=foz')
user@local$ DEBUG=nock.scope:example.com node my_test.js
...
nock.scope:example.com Interceptor queries: {"foo":"bar"} +1ms
nock.scope:example.com     Request queries: {"foo":"bar","baz":"foz"} +0ms
nock.scope:example.com query matching failed +0ms

Contributing

Thanks for wanting to contribute! Take a look at our Contributing Guide for notes on our commit message conventions and how to run tests.

Please note that this project is released with a Contributor Code of Conduct. By participating in this project you agree to abide by its terms.

Contributors

Thanks goes to these wonderful people (emoji key):

Pedro Teixeira
Pedro Teixeira

💻 🚧
n30n0v
n30n0v

💻
Richard Littauer
Richard Littauer

🚧 💻 📝
Ian Walker-Sperber
Ian Walker-Sperber

💻
Ivan Erceg
Ivan Erceg

💻 🚧
Paul Melnikow
Paul Melnikow

💻 🚧
Gregor Martynus
Gregor Martynus

💻 🚧 💼 💵 📝
Hutson Betts
Hutson Betts

💵
Jonas Lilja
Jonas Lilja

💵 💻
Benjamin Ki
Benjamin Ki

💵
Chad Fawcett
Chad Fawcett

💵

This project follows the all-contributors specification. Contributions of any kind welcome!

Sponsors

Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [Become a sponsor]

License

MIT

Copyright (c) 2011–2019 Pedro Teixeira and other contributors.

Comments
  • An in-range update of aws-sdk is breaking the build 🚨

    An in-range update of aws-sdk is breaking the build 🚨

    Version 2.250.1 of aws-sdk was just published.

    Branch Build failing 🚨
    Dependency aws-sdk
    Current Version 2.249.1
    Type devDependency

    This version is covered by your current version range and after updating it in your project the build failed.

    aws-sdk is a devDependency of this project. It might not break your production code or affect downstream projects, but probably breaks your build or test tools, which may prevent deploying or publishing.

    Status Details
    • continuous-integration/travis-ci/push The Travis CI build could not complete due to an error Details

    Release Notes Release v2.250.1

    See changelog for more information.

    Commits

    The new version differs by 3 commits.

    • eff4110 Updates SDK to v2.250.1
    • 6727985 Clean up a comment of max duration for DurationSeconds
    • a7f0422 Adds s3 virtual host-style tests

    See the full diff

    FAQ and help

    There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


    Your Greenkeeper Bot :palm_tree:

    opened by greenkeeper[bot] 63
  • Does not work with browserify

    Does not work with browserify

    Nock expects http.ClientRequest to be defined; the http module that browserify supplies does not use this internal object. Since ClientRequest is an internal object not typically used in an application space, I'm filing this as a bug -- just because you're CommonJS, doesn't mean you're running on node :)

    The error that exposes this issue:

    PhantomJS 1.9.2 (Linux) ERROR
            TypeError: 'undefined' is not an object (evaluating 'superCtor.prototype')
            at /tmp/karma-browerify.js:1740
    PhantomJS 1.9.2 (Linux): Executed 0 of 0 ERROR (12.249 secs / 0 secs)
    

    I've traced it down to line 161 of intercept.js.

    I understand if you consider nock to be just-for-node, and that extending nock to support mocking out browserify's http shim might be out of scope for the project. But it should probably be noted in the README.md that browserify is explicitly not supported (and, to be nice, suggest an alternative).

    bug 
    opened by cacieprins 50
  • 11.x release plan

    11.x release plan

    About 12 hours ago, Node 6 reached EOL! 🍾

    Should we plan to release 11.x?

    I've been testing the betas in all my projects and so far haven't run into problems. At one point I reached out to some of the projects that wrap nock and asked them to test on the betas. There are a lot of people using this library, though. Getting some more feedback might result in a smoother release.

    Would it be helpful to ship 11.0.0-rc.1 and announce widely that we want folks to test it?


    Pending issues:

    • [x] ~#1428 (PR needs work)~ #1588
    • [x] #1042 (no change was needed)
    • [x] #1564 ~(ready to merge)~
    • [x] Decide whether to proceed with #1596 (merged)
    • [x] Writing the release notes / upgrade guide
    • [ ] Do we want to schedule a window we publish 11.0.0 as next and ask people to test it?
    stale 
    opened by paulmelnikow 48
  • Enforce Order of Recordings

    Enforce Order of Recordings

    Context

    We use nockBack and would like to enforce the order of our recording files. Ideally there is a timeout option that can be set to allow for parallel recordings.

    Example: One request is made, then two other requests are made in parallel. Assuming a correct recording file already exists, the first request should match the first recorded request. However the second request might match the second or third recording. Ideally, if the request does not match the next recording, the request (nock) should wait for that recording to be consumed by any parallel requests.

    Alternatives

    Currently there doesn't seem to be any way to enforce this satisfactorily, let alone the parallel consideration mentioned above.

    Has the feature been requested before?

    Didn't find an anything surprisingly?

    If the feature request is accepted, would you be willing to submit a PR?

    Sure. As usually the biggest problem in this code base is figuring out where to make the change, so I'll need help with that :)

    feature stale 
    opened by simlu 48
  • Release 12.x

    Release 12.x

    I've opened a few PRs with breaking changes that I want to batch up for v12, though I suggest once that queue has been cleared we ship 12.x and resume working on improvements on master. In the past we used beta because we wanted to drop Node 6 and that would have severely held up development, though it would be a lot of overhead to maintain a beta and active branch and I don't think we have the capacity.

    I like the idea of using short-lived beta branches this way: solely for batching up breaking changes; extra points if we've implemented the breaking changes it in a backward-compatible way with shims which can simply be removed.

    opened by paulmelnikow 39
  • Next Maintainathon: Friday, January 18th!

    Next Maintainathon: Friday, January 18th!

    Let's have another one! Does this date work for you, @paulmelnikow and @jlilja? Gregor and I should be free for most of it (just talked to him). Anyone else want to join us, too, for a fun day of pair programming and collaborative hacking on Nock?

    I think the goal for next week ought to be getting our coverage metrics a bit higher.


    Update

    Gitter chat: https://gitter.im/nock/Lobby Zoom call: https://zoom.us/j/714137753

    Today’s instructions

    1. search the code base for "TODO-coverage": https://github.com/nock/nock/search?q=todo-coverage&unscoped_q=todo-coverage and just
    2. Pick which of the comments you’d like to work on
    3. Let us know which one you work on :)
    opened by RichardLitt 36
  • Test runner

    Test runner

    tap is a bit of a bear.

    It's slowing me down, though I'm not sure by how much.

    Mostly tap is unfamiliar; though an unfamiliar test runner is a still a hurdle to contribution.

    Some things it does well, like making sure a certain number of assertions are called or holding up a test until two things have each finished. Though those could be accomplished with Promise.all and an async test with an assertion on sinon.stub().

    Some of the things I find odd:

    1. The requirement for t.end().
    2. That it doesn't print a full stack trace on an exception.
    3. When things go wrong in some async code, the test just hangs. (Though maybe that would be fixed by an unhandled promise rejection handler?)
    4. Sometimes tests hang when everything seems to be fine, on account of the runner – because I've miscounted t.plan or not put the t.end in the right place. IIRC this is something @jlilja and @RichardLitt were running into as well.

    There are a chunk of data-driven tests in the codebase (maybe mostly in test_common.js?) In other projects I've taken to writing tests like those using Sazerac. It makes them much easier to read and write and easy to read when they fail. Unfortunately it doesn't support tap.

    If we decided to migrate, I imagine we'd need to mix two runners for a while. That seems like a bit of a drag. Though tap is so unopinionated, I imagine there is a way to make it coexist with another runner.

    I'm most familiar with Mocha, and especially like the way it handles async. Had a couple bad run-ins with Jest recently (metabolize/apollo-resolver-gcs#4), though I'm game to give it another try, especially if someone wants to champion it.

    Could also wait this out – to see if Jest fixes these issues or if we get used to tap.

    released 
    opened by paulmelnikow 32
  • Nock Office Hours

    Nock Office Hours

    Hey ya'll. 👋 @gr2m and I have been thinking about instituting Nock office hours, once a month, in order to give the maintainers and users a chance to air simple questions in a friendly way, and in order to promote more camaraderie among the community. We would pick a date, and set up a Zoom call on a regular schedule that anyone could jump on. The idea would be that it would last around a half hour or so.

    Would anyone be interested in attending such a session?

    pull request welcome 
    opened by RichardLitt 29
  • Remove AIRPLANE mode for testing - make all tests run offline

    Remove AIRPLANE mode for testing - make all tests run offline

    41 tests are currently skipped when run with AIRPLANE=true. Most if not all of these tests depend on external websites, which means if they change their behavior or are temporarily down, our tests will fail.

    Instead of depending on outside websites I suggest we spawn our own servers as we need them, it is simple enough.

    See #1083 for an example pull request / change set.

    I think these are all occurrences where we use the {skip: process.env.AIRPLANE} setting.

    If you’d like to help out, great! Please do the following

    1. comment below which one of these (can be multiple) you want to work on
    2. create an issue with the list of occurrences you want to work on and reference this issue (#1077)
    3. Then update your comment below with a link to that issue, I’ll keep the "Available" list updated

    Claimed / in progress

    🤷‍♀️

    Available

    🤷‍♂️

    Done

    good first issue pull request welcome released released on @beta 
    opened by gr2m 28
  • test: Update got to the latest version and fill in missing coverage

    test: Update got to the latest version and fill in missing coverage

    The devDependency got was updated from 9.6.0 to 10.0.0.

    This version is not covered by your current version range.

    If you don’t accept this pull request, your project will work just like it did before. However, you might be missing out on a bunch of new features, fixes and/or performance improvements from the dependency update.


    Publisher: sindresorhus License: MIT

    Release Notes for v10.0.0

    We're excited to announce Got 10! 🎉 This release has been in the works for almost a year and has been a major undertaking. Got was fully rewritten in TypeScript, which helped us catch many bugs and will give us more confidence in the codebase going forward. Got is now faster and much more stable. We also fixed a huge amount of bugs. Big thanks to everyone that helped make this release possible. 🙌


    If you find Got useful, you might want to sponsor the Got maintainers.

    Note: Some HTTP agents like https-proxy-agent and agentkeepalive are not compatible with Node.js 10 and hence not compatible with Got as Got takes advantage of some Node.js 10-only APIs.

    Breaking

    • Require Node.js 10 633651f
      • Why: This is so that we can use stream.pipeline for more reliable stream handling. Node.js 8 will be out of LTS at the end of this month anyway.
    • Remove support for protocol-less URLs in the url argument 92bc808
      • Why: To reduce ambiguity. It was not clear from just reading the code what it would default to.
      • Migrate:
    - got('sindresorhus.com');
    + got('https://sindresorhus.com');
    • Rename the query option to searchParams and make it stricter b223663 5376216 518f0f5
      • Why: To get closer to the window.fetch naming in the browser.
      • Migrate:
    - got(…, {query: …});
    + got(…, {searchParams: …});
    • Replace the baseUrl option with prefixUrl (#829) 0d534ed
      • Note: We also made it stricter to reduce ambiguity. The Got url argument now cannot be prefixed with a slash when this option is used.
      • Why: We renamed it to make it clear that it doesn't do any URL resolution.
      • Migrate:
    - got('/foo', {baseUrl: 'https://x.com'});
    + got('foo', {prefixUrl: 'https://x.com'});
    • Change the json option to accept an object instead of a boolean and to only be responsible for the request, not the response (#704) a6a7d5a
      • Note: You now set the request body in this option instead of the body option when you want to send JSON. This option also no longer sets the response type to JSON. You either call the .json() method or specify the responseType option for that.
      • Why: Many people were confused how {json: true} worked and they also complained that they could not set the request/response type individually.
      • Migrate:
    - got(url, {body: {x: true}, json: true});
    + got.post(url, {json: {x: true}}).json();
    • Use the responseType option instead of encoding to get a Buffer (#940) 6cc3d9f
      • Why: Previously, you would pass {encoding: null} to get a Buffer, but this was confusing. You now use {responseType: 'buffer'} instead.
      • Tip: You can also use got(…).buffer();.
      • Migrate:
    - got(…, {encoding: null});
    + got(…, {responseType: 'buffer'});
    • Don't infer POST automatically when specifying body (#756) e367bdb
      • Why: We're trying to reduce the amount of magic behavior.
      • Migrate:
    - got(…, {body: 'foo'});
    + got.post(…, {body: 'foo'});
    • The retries.retry option was split into retries.limit and retries.calculateDelay b15ce1d
      • Migrate:
     got(…, {
     	retry: {
    -		retries: 2
    +		limit: 2
     	}
     });
     got(…, {
     	retry: {
    -		retries: iteration => iteration < 2
    +		calculateDelay: ({attemptCount}) => attemptCount < 2
     	}
     });
     got(…, {
     	headers: {
    -		'user-agent': null
    +		'user-agent': undefined
     	}
     });
    • Rename the Promise API property .fromCache to .isFromCache (#768) b5e443b
    • Rename the stream option to isStream 518f0f5
      • Why: To make it clearer that it's a boolean and that it doesn't expect a stream to be passed in.
      • Migrate:
    - got(…, {stream: true});
    + got(…, {isStream: true});
    • Don't include the Got version in the default user-agent header (#911) 95bed1e
      • got/9.6.0 (https://github.com/sindresorhus/got)got (https://github.com/sindresorhus/got)
      • Why: Importing package.json to get the version caused a lot of problems. And you should ideally set your own user-agent header anyway.
    • Remove got.create() 518f0f5
      • You can achieve the same thing with got.extend() now.
    • Remove got.mergeInstances() 518f0f5
      • Use gotInstance.extend(...gotInstances) instead.
    • Move top-level error properties into an .options and .response property (#773) 6eaa81b
      • Migrate:
    - error.gotOptions
    + error.options
    

    - error.headers + error.response.headers

    - error.statusCode + error.response.statusCode

    - error.statusMessage + error.response.statusMessage

    - error.body + error.response.body

    - error.redirectUrls + error.response.redirectUrls

    - error.host + error.options.host

    - error.hostname + error.options.hostname

    - error.method + error.options.method

    - error.protocol + error.options.protocol

    - error.url + error.options.url

    - error.path + error.options.path

    • Custom instance creation was simplified (#707) 8eaef94
      • Note: got.mergeInstances(...instances) is deprecated. Use instanceA.extend(instanceB) instead.
      • Migrate:
    # Merging instances
    - got.mergeInstances(instanceA, instanceB, instanceC, …);
    + instanceA.extend(instanceB, instanceC, …);
    

    # Merging options - instanceA.extend(optionsB).extend(optionsC).extend(…); + instanceA.extend(optionsB, optionsC, …);

    # Merging instances and options - got.mergeInstances(instanceA.extend(optionsB), instanceC); + instanceA.extend(optionsB, instanceC, …);

    # Extending handlers - got.mergeInstances(instanceA, got.create({handler: handlerB})); + instanceA.extend({handlers: [handlerB]});

    Enhancements

    Fixes

    • Fix parsing response when using afterResponse hook (#775) e2054cd
    • Fix port not being reset on redirect (#729) ada5861
    • Fix the retry functionality (#787) 0501e00
    • Fix default retry option value when specifying a number (#809) 9c04a7c
    • Correctly handle promise- and stream-specific errors in the beforeError hook 134c9b7
    • Don't throw on early lookups 4faf5c7
    • Fix Node.js 13 compatibility (#915) b0dfc95
    • Fix memory leak when using cache feature (#792) 518f0f5
    • Don't throw on 204 No Content when parsing response (#925) 518f0f5
    • When redirect fails, don't retry from scratch (#930) 518f0f5
    • Retrying inside afterResponse hook should trigger beforeRetry hook (#918) 518f0f5
    • Fix a bug that sometimes caused the Node.js process to hang 518f0f5
    • Fix a bug where cookies weren't reset on redirect between two different sites 518f0f5
    • Make the progress events not be based on internal Node.js properties cd11a50

    Docs

    • Clarify retry behavior 5e6782a
    • Clarify prefixUrl behavior (#943) f008bc9
    • Document that retry option doesn't work with streams 9088866
    • Encourage using Stream.pipeline() when using the stream API 06afb27
    • Add instructions for global-agent (#822) ca8c560
    • Mention the TimeoutError.timings property 8fa18f4
    • Mention how to abort the request using hooks 96ea75f

    All commits

    v9.6.0...v10.0.0

    Commits

    The new version differs by 163 commits.

    • abdfee2 10.0.0
    • aae7b89 Improve readme
    • 71b8452 Improve types (#946)
    • f008bc9 Clarify prefixUrl behavior (#943)
    • d968e49 Upgrade dependencies
    • b82358f Add methodRewriting option (#942)
    • 966e7ff 10.0.0-beta.2
    • c537dee Make TypeScript types conforms to strict mode (#928)
    • d9a3273 Clarify retry behavior (#944)
    • 6cc3d9f Use responseType instead of options.encoding to get a Buffer (#940)
    • 3acdb69 Update a comment
    • d0f2dfd Mention that progress.total can be undefined
    • 8874a45 Preserve stacktrace when wrapping errors (#935)
    • b7a356a Fix .json() usage in readme.md
    • 2c5d0c0 Follow-up to cd11a5092972c16e5295f4d13dee20c228dcb19c

    There are 163 commits in total.

    See the full diff


    FAQ and help

    There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


    Your Greenkeeper bot :palm_tree:

    released breaking-change 
    opened by greenkeeper[bot] 26
  • Replace lodash with native where appropriate

    Replace lodash with native where appropriate

    I noticed there are a lot of uses of lodash. I think where lodash shines is in avoiding reimplementation of functions like mapValues within the codebase. However there are a lot of places in the codebase where native functions like Array#map, Object.assign, and Array.from(new Set(...)) would be equally understandable, and without the added layer of a library function.

    A borderline case is _.flat which can be replaced by Array#reduce.

    How are we feeling about code coverage? Is it important to nudge up the coverage before making changes like this in library code?

    Related: This is still marked as supporting Node 4 in package.json. The tests run on 6+, and @gr2m I've heard you say you're just about ready to drop support for Node 6. Is there a roadmap or timeline in mind? I'm wondering if we can go right to object spread instead of using Object.assign.

    pull request welcome 
    opened by paulmelnikow 25
  • chore(deps-dev): bump @definitelytyped/dtslint from 0.0.112 to 0.0.142

    chore(deps-dev): bump @definitelytyped/dtslint from 0.0.112 to 0.0.142

    Bumps @definitelytyped/dtslint from 0.0.112 to 0.0.142.

    Release notes

    Sourced from @​definitelytyped/dtslint's releases.

    v0.0.142

    Highlights

    The PRs below convert 3 more lint rules from TSLint to ESLint.

    What's Changed

    New Contributors

    Full Changelog: https://github.com/microsoft/DefinitelyTyped-tools/compare/v0.0.141...v0.0.142

    v0.0.141

    What's Changed

    Full Changelog: https://github.com/microsoft/DefinitelyTyped-tools/compare/v0.0.140...v0.0.141

    v0.0.140

    What's Changed

    Full Changelog: https://github.com/microsoft/DefinitelyTyped-tools/compare/v0.0.139...v0.0.140

    v0.0.139

    ... (truncated)

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    dependencies javascript 
    opened by dependabot[bot] 0
  • chore(deps-dev): bump mocha from 9.2.2 to 10.2.0

    chore(deps-dev): bump mocha from 9.2.2 to 10.2.0

    Bumps mocha from 9.2.2 to 10.2.0.

    Release notes

    Sourced from mocha's releases.

    v10.2.0

    10.2.0 / 2022-12-11

    :tada: Enhancements

    • #4945: API: add possibility to decorate ESM name before import (@​j0tunn)

    :bug: Fixes

    :book: Documentation

    v10.1.0

    10.1.0 / 2022-10-16

    :tada: Enhancements

    :nut_and_bolt: Other

    v10.0.0

    10.0.0 / 2022-05-01

    :boom: Breaking Changes

    :nut_and_bolt: Other

    ... (truncated)

    Changelog

    Sourced from mocha's changelog.

    10.2.0 / 2022-12-11

    :tada: Enhancements

    • #4945: API: add possibility to decorate ESM name before import (@​j0tunn)

    :bug: Fixes

    :book: Documentation

    10.1.0 / 2022-10-16

    :tada: Enhancements

    :nut_and_bolt: Other

    10.0.0 / 2022-05-01

    :boom: Breaking Changes

    :nut_and_bolt: Other

    ... (truncated)

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    dependencies javascript 
    opened by dependabot[bot] 0
  • chore(deps-dev): bump @sinonjs/fake-timers from 10.0.0 to 10.0.2

    chore(deps-dev): bump @sinonjs/fake-timers from 10.0.0 to 10.0.2

    Bumps @sinonjs/fake-timers from 10.0.0 to 10.0.2.

    Changelog

    Sourced from @​sinonjs/fake-timers's changelog.

    10.0.2 / 2022-12-15

    • Revert change to lock file. Back on v2

    10.0.1 / 2022-12-15

    • fix: requestAnimationFrame args (#458)
    • chore: remove fsevents dependency (#455)
    • Upgrade dependencies (#440)
    • fix(runToLastAsync): tick the correct amount of time
    • Fix #441: update spelling of prop descriptor prop (#453)
    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    dependencies javascript 
    opened by dependabot[bot] 0
  • chore(deps-dev): bump sinon from 14.0.1 to 15.0.1

    chore(deps-dev): bump sinon from 14.0.1 to 15.0.1

    Bumps sinon from 14.0.1 to 15.0.1.

    Changelog

    Sourced from sinon's changelog.

    15.0.1

    • aa493da4 Upgrade to fake-timers v10.0.2 (Carl-Erik Kopseng)

      Contains several fixes

    • b3ee0aa5 Use Node version 18 in Runkit examples (Carl-Erik Kopseng)

    Released by Carl-Erik Kopseng on 2022-12-15.

    15.0.0

    • b75fbfa9 Fix 2448: remove custom formatter (Morgan Roderick)

      Remove option to pass a custom formatter.

      The sub libraries of Sinon has long moved on to use util.inspect from Node. By using that in Sinon itself, we align all the libraries.

    Released by Morgan Roderick on 2022-11-28.

    14.0.2

    • 4d70f6e0 Upgrade nise to latest (Morgan Roderick)
    • 96a0d756 Update @​sinonjs/samsam to latest (Morgan Roderick)
    • babb4736 Prefer @​sinonjs/commons@​2 (Morgan Roderick)

      That makes ES2017 support explicit

    Released by Morgan Roderick on 2022-11-07.

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    dependencies javascript 
    opened by dependabot[bot] 0
  • delayConnection and delayBody delays too much when used with npm got

    delayConnection and delayBody delays too much when used with npm got

    Please avoid duplicates

    • [X] I checked all open bugs and none of them matched my problem.

    Reproducible test case

    documented below

    Nock Version

    13.2.9

    Node Version

    v16.13.0

    TypeScript Version

    n/a

    What happened?

    const got = require('got');
    const nock = require("nock");
    
    
    function log(msg) {
        console.log(`${(new Date()).toUTCString()} - ${msg}`);
    }
    function setupNock(issuer) {
        nock(issuer)
            .persist()
            .get("/.well-known/openid-configuration")
            .delayConnection(3000)
            .reply(200, {test: "dummy"});
    }
    
    async function getDiscovery(issuer) {
        issuer += issuer.endsWith("/") ? "" : "/";
        const res = await got.get(issuer + ".well-known/openid-configuration", {responseType: "json", timeout: 2000});
        return res.body;
    }
    
    async function main() {
        log("start");
        const issuer = "https://accounts.google.com";
    
    
        setupNock(issuer);
    
        log("before call");
        const discovery = await getDiscovery(issuer).catch(e => log("the err: " + JSON.stringify(e)));
        log("discovery: " + JSON.stringify(discovery));
        log("end");
    }
    
    main();
    

    Canonical code example that reproduces the issue.

    Description: When setting nock delayConneciton to e.g. 3000ms but setting got to timeout after 2000, got will throw exception indicating that the request waited for ~2000ms but in practice the request will return after ~9seconds not sure why

    Log output explains the code above execution:

    Wed, 21 Dec 2022 11:16:32 GMT - start
    Wed, 21 Dec 2022 11:16:32 GMT - before call
    Wed, 21 Dec 2022 11:16:41 GMT - the err: {"name":"TimeoutError","code":"ETIMEDOUT","timings":{"start":1671621399837,"socket":1671621399837,"lookup":1671621399837,"connect":16716213
    99837,"secureConnect":1671621399837,"upload":1671621399837,"error":1671621401851,"phases":{"wait":0,"dns":0,"tcp":0,"tls":0,"request":0,"total":2014}},"event":"request"}
    Wed, 21 Dec 2022 11:16:41 GMT - discovery: undefined
    Wed, 21 Dec 2022 11:16:41 GMT - end
    
    Process finished with exit code 0
    

    You can see that first print was on 11:16:32 and the exception received at 11:16:41 = 9 seconds when got configured to 2000ms

    This is even stranger when we set delayBody - the exception will take time to occur as well but got will show that the total amount of time waited is ~9000 ms instead of the timeout configured to got.

    Would you be interested in contributing a fix?

    • [ ] yes
    bug 
    opened by glenlvy 1
  • Proposal: Promise based version of scope.isDone()

    Proposal: Promise based version of scope.isDone()

    Please avoid duplicates

    Context

    There are times when it would be convenient to await the completion of network requests. This would be a promise based version of scope.isDone(). It would resolve when the scope is done and network requests have been sent.

    When used in combination of the act() test utilities this could provide a compact way to assert that a response has been sent and component updates are complete.

    const searchResults = nock(API_HOST)
      .post(SEARCH_ROUTE, searchMatcher)
      .reply(searchResultReply)
    
    // ... a component is rendered that makes a request
    
    await act(async () => {
      await searchResults.isDoneP();
    });
    
    expect(
      renderer.root.findAllByType('span').map(({children}) => children)
    ).toEqual(
      [....]
    );
    

    Alternatives

    Here's a draft of a function that provides similar functionality:

    async function nockScopeResolved(scope, ms = 500, triesLeft = 5) {
      return new Promise((resolve, reject) => {
        const interval = setInterval(async () => {
          if (scope.isDone()) {
            resolve();
            clearInterval(interval);
          } else if (triesLeft <= 1) {
            reject();
            clearInterval(interval);
          }
          triesLeft--;
        }, ms);
      });
    }
    

    can be used as

    const searchResults = nock(API_HOST)
      .post(SEARCH_ROUTE, searchMatcher)
      .reply(searchResultReply)
    
    await nockScopeResolved(searchResults);
    

    If the feature request is accepted, would you be willing to submit a PR?

    • [X] yes
    feature 
    opened by craig-davis 0
Releases(v13.2.9)
Owner
Nock
HTTP Mocking for Node.js
Nock
🌐 Human-friendly and powerful HTTP request library for Node.js

Sindre's open source work is supported by the community. Special thanks to: Human-friendly and powerful HTTP request library for Node.js Moving from R

Sindre Sorhus 12.5k Jan 9, 2023
An HTTP Web Server for Chrome (chrome.sockets API)

An HTTP Web Server for Chrome (chrome.sockets API)

Kyle Graehl 1.2k Dec 31, 2022
Async node.js implementation of the UDP Minecraft Server Query Protocol and TCP Minecraft Server List Ping Protocol

?? Mc Server Status Async node.js implementation of the UDP Minecraft Server Query Protocol and TCP Minecraft Server List Ping Protocol. Also availabl

Daniel 5 Nov 10, 2022
Promise based HTTP client for the browser and node.js

axios Promise based HTTP client for the browser and node.js New axios docs website: click here Table of Contents Features Browser Support Installing E

axios 98k Dec 31, 2022
Ajax for Node.js and browsers (JS HTTP client)

superagent Small progressive client-side HTTP request library, and Node.js module with the same API, supporting many high-level HTTP client features T

Sloth 16.2k Jan 1, 2023
Full-featured, middleware-oriented, programmatic HTTP and WebSocket proxy for node.js

rocky A multipurpose, full-featured, middleware-oriented and hackable HTTP/S and WebSocket proxy with powerful built-in features such as versatile rou

Tom 370 Nov 24, 2022
Very very very powerful, extensible http client for both node.js and browser.

ES-Fetch-API 中文 | English Very very very powerful, extensible http client for both node.js and browser. Why should you use ES-Fetch API? Still using a

null 17 Dec 12, 2022
A full-featured http proxy for node.js

node-http-proxy node-http-proxy is an HTTP programmable proxying library that supports websockets. It is suitable for implementing components such as

http ... PARTY! 13.1k Jan 3, 2023
Simplifies node HTTP request making.

Requestify - Simplifies node HTTP request making. Requestify is a super easy to use and extendable HTTP client for nodeJS + it supports cache (-:. Ins

Ran Mizrahi 222 Nov 28, 2022
Run HTTP over UDP with Node.js

nodejs-httpp - Run HTTP over UDP based transport and Bring Web in Peer or P2P styles main js modules: udt.js, httpp.js, udts.js and httpps.js, that's

AppNet.Link 142 Aug 2, 2022
Library agnostic in-process recording of http(s) requests and responses

@gr2m/http-recorder Library agnostic in-process recording of http(s) requests and responses Install npm install @gr2m/http-recorder Usage import http

Gregor Martynus 4 May 12, 2022
🏊🏾 Simplified HTTP request client.

Deprecated! As of Feb 11th 2020, request is fully deprecated. No new changes are expected to land. In fact, none have landed for some time. For more i

request 25.6k Jan 4, 2023
make streaming http requests

hyperquest treat http requests as a streaming transport The hyperquest api is a subset of request. This module works in the browser with browserify. r

James Halliday 711 Sep 8, 2022
HTTP Client Utilities

@hapi/wreck HTTP client utilities. wreck is part of the hapi ecosystem and was designed to work seamlessly with the hapi web framework and its other c

hapi.js 383 Nov 1, 2022
Wrap native HTTP requests with RFC compliant cache support

cacheable-request Wrap native HTTP requests with RFC compliant cache support RFC 7234 compliant HTTP caching for native Node.js HTTP/HTTPS requests. C

Luke Childs 259 Dec 20, 2022
Global HTTP/HTTPS proxy agent configurable using environment variables.

global-agent Global HTTP/HTTPS proxy configurable using environment variables. Usage Setup proxy using global-agent/bootstrap Setup proxy using bootst

Gajus Kuizinas 267 Dec 20, 2022
HTTP Client for Visual Studio Code to POST JSON, XML, image, ... files to REST APIs

friflo POST Goal Main goal of this extension is storing all HTTP request & response data automatically as files in a VSCode workspace. This ensures th

Ullrich Praetz 2 Nov 18, 2021
SPDY server on Node.js

SPDY Server for node.js With this module you can create HTTP2 / SPDY servers in node.js with natural http module interface and fallback to regular htt

SPDY & HTTP2 in JavaScript 2.8k Jan 4, 2023
Run Node.js on Android by rewrite Node.js in Java

node-android Run Node.js on Android by rewrite Node.js in Java with the compatible API. third-party: libuvpp, libuv-java JNI code by Oracle. Build Clo

AppNet.Link 614 Nov 15, 2022