Giter Site home page Giter Site logo

superagent-mock's Introduction

npm npm bundle size npm GitHub last commit NPM NPM Downloads Continous Integration

Installation | Usage | Supported Methods | Credits | License

superagent-mock

superagent plugin allowing to simulate HTTP calls by returning data fixtures based on the requested URL.

See this post to know why we use superagent-mock at Bedrock Streaming.

Installation

Install with npm: npm install superagent-mock

Install with yarn: yarn add superagent-mock

Requirements

node >= 8.0 superagent >= ^3.6.0

Usage

First, you have to define the URLs to mock in a configuration file:

// ./superagent-mock-config.js file
module.exports = [
  {
    /**
     * regular expression of URL
     */
    pattern: 'https://domain.example(.*)',

    /**
     * returns the data
     *
     * @param match array Result of the resolution of the regular expression
     * @param params object sent by 'send' function
     * @param headers object set by 'set' function
     * @param context object the context of running the fixtures function
     */
    fixtures: function (match, params, headers, context) {
      /**
       * Returning error codes example:
       *   request.get('https://domain.example/404').end(function(err, res){
       *     console.log(err); // 404
       *     console.log(res.notFound); // true
       *   })
       */
      if (match[1] === '/404') {
        throw new Error(404);
      }

      /**
       * Checking on parameters example:
       *   request.get('https://domain.example/hero').send({superhero: "superman"}).end(function(err, res){
       *     console.log(res.body); // "Your hero: superman"
       *   })
       */

      if (match[1] === '/hero') {
        if(params['superhero']) {
          return 'Your hero:' + params['superhero'];
        } else {
          return 'You didnt choose a hero';
        }
      }


      /**
       * Checking on headers example:
       *   request.get('https://domain.example/authorized_endpoint').set({Authorization: "9382hfih1834h"}).end(function(err, res){
       *     console.log(res.body); // "Authenticated!"
       *   })
       */

      if (match[1] === '/authorized_endpoint') {
        if(headers['Authorization']) {
          return 'Authenticated!';
        } else {
          throw new Error(401); // Unauthorized
        }
      }

      /**
       * Cancelling the mocking for a specific matched route example:
       *   request.get('https://domain.example/server_test').end(function(err, res){
       *     console.log(res.body); // (whatever the actual server would have returned)
       *   })
       */

      if (match[1] === '/server_test') {
        context.cancel = true; // This will cancel the mock process and continue as usual (unmocked)
        return null;
      }

      /**
       * Delaying the response with a specific number of milliseconds:
       *   request.get('https://domain.example/delay_test').end(function(err, res){
       *     console.log(res.body); // This log will be written after the delay time has passed 
       *   })
       */

      if (match[1] === '/delay_test') {
        context.delay = 3000; // This will delay the response by 3 seconds
        return 'zzZ';
      }

      /**
       * Mocking progress events:
       *   request.get('https://domain.example/progress_test')
       *     .on('progress', function (e) { console.log(e.percent + '%'); })
       *     .end(function(err, res){
       *       console.log(res.body); // This log will be written after all progress events emitted 
       *     })
       */

      if (match[1] === '/progress_test') {
        context.progress = {
          parts: 3,               // The number of progress events to emit one after the other with linear progress
                                  //   (Meaning, loaded will be [total/parts])
          delay: 1000,            // [optional] The delay of emitting each of the progress events by ms 
                                  //   (default is 0 unless context.delay specified, then it's [delay/parts])
          total: 100,             // [optional] The total as it will appear in the progress event (default is 100)
          lengthComputable: true, // [optional] The same as it will appear in the progress event (default is true)
          direction: 'upload'     // [optional] superagent adds 'download'/'upload' direction to the event (default is 'upload')
        };
        return 'Hundred percent!';
      }
    },

    /**
     * returns the result of the GET request
     *
     * @param match array Result of the resolution of the regular expression
     * @param data  mixed Data returns by `fixtures` attribute
     */
    get: function (match, data) {
      return {
        body: data
      };
    },

    /**
     * returns the result of the POST request
     *
     * @param match array Result of the resolution of the regular expression
     * @param data  mixed Data returns by `fixtures` attribute
     */
    post: function (match, data) {
      return {
        status: 201
      };
    }
  },
  ...
];

Then use the plugin:

// ./server.js file
var request = require('superagent');
var config = require('./superagent-mock-config');

// Before tests
var superagentMock = require('superagent-mock')(request, config);

...

// After tests
superagentMock.unset();

Supported methods

All request methods are supported (get, put, post, etc.).

Each request method mock have to be declared in the config file. Otherwise, the callback method is used.

Logging

You can monitor each call, that has been intercepted by superagent-mock or not, by passing a callback function at initialization.

// ./server.js file
var request = require('superagent');
var config = require('./superagent-mock-config');

var logger = function(log)  {
  console.log('superagent call', log);
};

// Before tests
var superagentMock = require('superagent-mock')(request, config, logger);

...

// After tests
superagentMock.unset();

The callback function will be called with an object containing the following informations

  • data : data used with superagent.send function
  • headers : array of headers given by superagent.set function
  • matcher : regex matching the current url which is defined in the provided config
  • url : url which superagent was called
  • method : HTTP method used for the call
  • timestamp : timestamp of the superagent call
  • mocked : true if the call was mocked by superagent mock, false if it used superagent real methods

Development scripts

To run units tests: yarn test.

To check code style: yarn lint.

To build code: yarn build.

Credits

Developped by the Cytron Team of Bedrock Streaming. Tested with Jest.

License

superagent-mock is licensed under the MIT license.

superagent-mock's People

Contributors

awei01 avatar bernard-leech avatar bmancini42 avatar clayreimann avatar d34thwings avatar dependabot[bot] avatar devside avatar elisherer avatar fdubost avatar ihorbond avatar jhenriquez avatar lylejohnson avatar mosson avatar natelaws avatar oponder avatar oziks avatar slashgear avatar tetsu9901 avatar tomazzaman avatar widdershin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

superagent-mock's Issues

matcher is not working

It seems the matcher is not working, here is my config

export default [
  {
    /**
     * regular expression of URL
     */
    pattern: process.env.REACT_APP_API_ENDPOINT,

    /**
     * returns the data
     *
     * @param match array Result of the resolution of the regular expression
     * @param params object sent by 'send' function
     * @param headers object set by 'set' function
     * @param context object the context of running the fixtures function
     */
    fixtures: function(match, params, headers, context) {
      if (/\/events\/[\d|\w]+$/.test(match[1])) {
        return {
          title: "Test"
        }
      }
    },

    /**
     * returns the result of the GET request
     *
     * @param match array Result of the resolution of the regular expression
     * @param data  mixed Data returns by `fixtures` attribute
     */
    get: function(match, data) {
      return {
        body: data
      }
    },

    /**
     * returns the result of the POST request
     *
     * @param match array Result of the resolution of the regular expression
     * @param data  mixed Data returns by `fixtures` attribute
     */
    post: function(match, data) {
      return {
        status: 201
      }
    }
  }
]

The node env is equal to

https://example.com/api

I'm using a server setup like so

import request from "superagent"
import su from "superagent-use"
import prefix from "superagent-prefix"
import config from "../config/superagent-mock"
import superagentMock from "superagent-mock"

const agent = su(request)
agent.use(prefix(process.env.REACT_APP_API_ENDPOINT))

const logger = function(log) {
  console.log("superagent call", log)
}

export const setupMockServer = () => {
  return superagentMock(agent, config, logger)
}

And then I later in the code I'm testing make a request like so

return API.get(`/events/${eventUrl}`)
.then(response => {
  if (!response.ok) {
    throw Error(response.statusText)
  }
  return response
})
.then(response => response.body)

API here is a reference to a different file that the whole app uses

import request from "superagent"
import prefix from "superagent-prefix"

export default request.agent().use(prefix(process.env.REACT_APP_API_ENDPOINT))

An example full URL might behttps://example.com/api/events/1234

When I log out the match argument I see this in my jest terminal output

console.log('match', match)
...
match [ 'https://example.com/api',
        index: 0,
        input: 'https://example.com/api/events/1234' ]

It seems the implementation suggested in the config is not working

Mocking an error and using retry()

I'm trying to do the following

  1. Get a fixture to return a server error (500)
  2. Use retry(3) on the superagent
  3. See that the fixture is called 3 times, and the get request ultimately fails.

I've written a small test script, from what it looks like the failure happens immediately after the exeption even though the retry callback is called 3 times afterwards.

Here's my script

var request = require('superagent');
var config = [{
  pattern: 'http://localhost/500/devices',
  fixtures: (match, params) => {
    let err = new Error(500);
    err.status = 500;
    throw err;
  },
  get: (match, data) => {
      return data;
  }
}];


var superagentMock = require('superagent-mock')(request, config);

request
  .get('http://localhost/500/devices')
  .retry(3, retryCallback)
  .then(res => {
    console.log('completed');
   	console.log(res);
  })
  .catch(err => {
    console.log('failed');
    console.log(err);
  });


function retryCallback(err, res) {
  console.debug('__retrying');
  return true;
}

Output is
failed
__retrying
__retrying
__retrying

Is there an error with how superagent-mock interacts with retry or am I approaching this the wrong way?

Stream support

I want to mock a superagent request that creates pipes the data to a write stream e.g.

const targz = require('tar.gz2');
const request = require('superagent');

const tarball = 'some-file.tgz';
const read = request.get(tarball);
const write = targz().createWriteStream(dir);

const stream = read.pipe(write).on('finish', doSomething);

No support for DELETE?

{
    pattern: '/api/bookmarks$',
    fixtures: function(match, params) {
        if (params === undefined || parseInt(params.question_id) <= 0) {
            var message = 'a valid question_id in params is required';
            var error = new Error(message);
            error.response = message;
            error.status = 400;
            throw error;
        }
    },
    post: function(match, data) {   // bookmark question
        return data;
    },
    del: function(match, data) {    // unbookmark question
        return data;
    }
}

In the above example, POST works, but DELETE doesn't; the function 'del' is never called, but 'fixtures' is, and 'post' is called for POST just fine. This commit looked promising as it mentioned adding explicit support for DELETE, but reading through it, I don't believe it actually does.

The .set function is not loyal to the superagent implementation

The oldSet function call under .end passes an object (this.headers) which in superagent's implementation does a for loop to insert them (by calling the set with key and value), but in superagent, this.set refers to the mock client set function which makes no difference to the internal state.

The side effect is that type() and set() don't work when using mock.

hope it's clear.

Response error when no matching URL found

When a URL is requested that does not have a matching definition/method, the response is returned with the error/status 'parserMethod is not a function.' if a callback is not defined. I don't believe this dependency is documented, nor is the fact you can set a callback.

Assuming the error needs to be properly handled, and the documentation updated.

Happy to submit a PR if needs be.

Cannot read properties of undefined (reading 'prototype')

Hey thanks for this package! I have the following issue: when it's used with https://www.npmjs.com/package/supertest
the following line breaks
https://github.com/BedrockStreaming/superagent-mock/blob/v5.0.0/src/superagent-mock.js#L15

because the agent object exposed by supertest doesn't have a Request property, I'm assuming that's the issue
https://github.com/ladjs/supertest/blob/v6.3.3/lib/agent.js#L51

I know that it sounds like a supertest issue but was wondering if you'd be interested to update you plugin as well ?

Issue when used with superagent-promise...

When I use this with superagent-promise I get this error: Unhandled promise rejection TypeError: parserMethod is not a function(…)(anonymous function) @ es6.promise.js:112

I have no idea why! :P

v2.0.1: TypeError: first argument must be a string or Buffer

After upgrading superagent-mock from 1.12.0 -> 2.x, all my unit tests which has a request.post.send

request.post(login_url)
  .send({
     username: username,
     token: token
  }).then((res) =>
     // assertions
  )

are failing with the following error

TypeError: first argument must be a string or Buffer 

at ClientRequest.OutgoingMessage.end (_http_outgoing.js:542:11)
      at Request.end (/opt/default/lib/node_modules/superagent/lib/node/index.js:873:9)
      at Request.end (/opt/default/lib/node_modules/superagent-mock/lib/superagent-mock.js:153:21)
      at /opt/default/lib/node_modules/superagent/lib/request-base.js:72:12
      at Request.then (/opt/default/lib/node_modules/superagent/lib/request-base.js:71:31)

The same code works fine if I revert to 1.12.0. I am using superagent v2.3.0 and superagent-mock v2.0.1.

retry is not a function

TypeError: _superagent2.default.post(...).send(...).accept(...).retry is not a function

request
      .post(endpoint)
      .send(body)
      .accept('application/json')
      .retry(3)

"react-native": "0.47.1"
"superagent": "3.3.1"
"superagent-mock": "3.5.0"

using superagent-mock with agent

Hi there,

I started using and superagent agent (ladjs/superagent#1302) to have a general interceptor for my request

const requestAgent = request.agent().use(AuthIntercept);

But now I do not know how to mock that agent using superagent-mock. if I do:

import requestAgent from '../../utils/superagentInterceptor';
import superagentMock from 'superagent-mock';

superagentMockTest = superagentMock(requestAgent, configGood);

when running the test I get the error undefined is not an object (evaluating 'Request.prototype'). I don't know if I am using it wrong or still does not have support to mock agents . Please advice

.set() does not behave as the original superagent.set()

With superagent, it is possible to set() headers one by one and chain the set() calls.

With superagent-mock, this is not handled and it is mandatory to make a single call with a full header object.
I'm using 1.7.0 from npm.

How do I get multi-part data from mock config file

Hello guys,

From the below, how do I get the content of formData in a mock config file.

var formData = new FormData();
formData.append('image', file);

superagent( "POST", `<some-url>` )
        .send(formData)

here is what I have.

{
            pattern: `<some-url>`,
            fixtures: function (match, params, headers) {
                console.log(match, params, headers);
            },
            post: function (match, data) {
}

when the content of match, params and headers gets logged, the value of formData is not present.

Thanks

Response body on errors

I'm testing a simple api service, with a bit of help of http://reqr.es/

Here is my mock config:

export default [{
  pattern: 'http://reqr.es(.*)',

  // Fixtures return data
  fixtures: function( match, params, headers ) {

    if( match[1] === '/api/login' ) {
      if( params['password'] ) {
        return { "token": "QpwL5tke4Pnpja7X" };
      }
      throw new Error( 400 )
    }
  },

  // Requests return responses
  post: function( match, data ) {
    return {
      code: 200,
      body: data
    };
  }
}]

Now if I throw error 400 (for making an invalid request to /api/login) I need to verify the error being returned (in this concrete case { "error": "Missing password" }).

How can I do that?

Requires valid host/destination?

Disregard please. Finding that it bypasses the config if any errors.

Was getting mocked: false in res, think I have found my issue.

Specs got stuck in the end

I'm using superagent-mock to mock my requests and, even returning the correct result and the tests are passing, after all specs are done, the suite got stuck and release after 60s (probably a timeout).

Disabling superagent-mock makes all the specs to pass (hitting the real server), and the suite finishes instantly.

Below is my spec:

  describe('search triggers', () => {
    const mockObj = superagentMock(request, rules)

    describe('city search', () => {
      beforeEach(() => triggerSearchByCity())

      it('should update the state', () => {
        expect(subject.state.loaded).toEqual(true)
        expect(subject.state.data).toEqual(apiMocks.cityResponse)
      })
    })

And my mock setup rules:

 rules= [{
    pattern: 'http://api.openweathermap.org/data/2.5/weather(.*)',

    
    fixtures: function (match, params, headers, context) {
      // City search mock
      if (match[1] === '?q=Berlin%2CDE&units=metric&APPID=[MY APP ID]') {
        return cityResponse
      }

    get: function (match, data) {
      return {
        body: data
      };
    }
  }
];

And my superagent call:

  return new Promise((resolve, reject) => {
    request
      .get('http://api.openweathermap.org/data/2.5/weather')
      .query({
        q: `Berlin,DE`,
        units: 'metric',
        APPID: '[My APP ID]'
      })
      .end((err, res) => {
        if (err) { reject (err)      }
        else     { resolve(res.body) }
      })
  })

The promise above is resolved in the triggerSearchByCity() method.

UPDATE:
Here's another simpler setup that causes the same problem:
https://gist.github.com/mauricioklein/b0dd5768bce0075232729596dc71a83f

Thanks!

Not compatible with 'superagent' >= 3.6.0

This module currently depends on a private function named _appendQueryString which was replaced with _finalizeQueryString in 3.6.0. This means that query strings are no longer available from fixture callback match argument.

How do you run the tests?

Hi,

I merged from the latest version and couldn't figure out how to run the tests..
All I get is:

yarn test v0.21.3
$ jest ./tests
No tests found
In C:\Private\superagent-mock
  8 files checked.
  testMatch: **/__tests__/**/*.js?(x),**/?(*.)(spec|test).js?(x) - 2 matches
  testPathIgnorePatterns: \\node_modules\\ - 8 matches
Pattern: "./tests" - 0 matches
Done in 4.28s.

Setting .end callback response argument to null

Is there any way to set the response argument in .end callback to null? I am trying to write a unit test to simulate when the api response is not json while superagent expects json (the default). This is for code that runs in the browser.

Test Examples

I am writing tests with enzyme and jest. It would be nice to see superagent-mock in some unit tests examples.

Better usage when mocking client

It's okay to write these two lines of code

var config = require('./superagent-mock-config'); 
require('superagent-mock')(request, config);

in test files (preprocessor, beforeEach or whatever) but when you use superagent-mock in a client app (running in the browser), you had to append these lines somewhere in your source files.

How about a webpack loader (a postLoader I think) ?

I'm not a webpack expert but I think it could do the job and keep your sources clean 🤘

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.