如何对HTTP请求进行单元测试?

时间:2017-10-02 18:23:02

标签: node.js unit-testing mocha chai

标题询问如何使用Mocha和Chai测试HTTP请求?

我最近开始学习单元测试,但我仍然对测试的某些方面感到困惑。我可以通过返回值的精细测试方法获得但我对如何测试发出HTTP / IO请求的方法感到困惑。

例如,我有以下代码:

module.exports = someRequest => new Promise((resolve, reject) => 
    http.get('http://google.com', resp => {
        if(resp.headers['content-type'] !== 200) {
            reject(new Error('Failed to connect to Google'));
        }
        resolve('Connected to Google');
    })
);

我想测试2个案例:

  1. 向Google提出的请求已成功
  2. 对Google的请求失败
  3. 我是否必须模拟这些请求?如果是,那么模拟用于发出HTTP请求的方法的目的是什么?

    先谢谢。

4 个答案:

答案 0 :(得分:2)

我过去曾使用supertest并对此感到非常满意

import server from '../src/server';
import Request from 'supertest';

describe('Server', () => {
  const request = Request(server());

  describe('/api', () => {
    it('Should return a 404 when invalid route', done => {
      request
        .post('/api/notfound')
        .expect(404)
        .end(done);
    });
  });
});

答案 1 :(得分:1)

用这样的东西嘲笑http.get怎么样?

const createHttpGetMock = (expectedStatus) => {
  return httpGetMock = (address) => {
    return new Promise((resolve, reject) => {
      resolve({
        status: expectedStatus,
        headers: {
          // ... headers
        },
        // mock response
      })
    })
  }
}

然后你的测试看起来像这样:

describe("Google request", () => {
  it("Resolves when google responds", async () => {
    const httpSuccessMock = createHttpGetMock(200);
    // Inject your mock inside your request function here, using your favorite lib

    const message = await fetchGoogle();
    assert.equals(message, 'Connected to Google');
  })

  it("Rejects when google responds with error", async () => {
    const httpSuccessMock = createHttpGetMock(500);
    // Inject your mock inside your request function here, using your favorite lib

    const message = await fetchGoogle();
    assert.equals(message, 'Failed to connect to Google');
  })
});

这将完成良好单元测试的基本合同:无论外部模块和依赖性如何,它都可以确保您当前正在测试的模块在每种可能的情况下都具有正确的行为。

答案 2 :(得分:1)

尝试Mockttp(免责声明:我是Mockttp的维护者)。

Mockttp为您提供了一个真正的本地HTTP服务器和代理,您可以轻松地将其配置并用于测试,以用于此类情况。

您可以使用本地主机URL直接向Mockttp发出请求,或者继续向google.com发出请求,但使用Mockttp作为代理,然后配置您要模拟的任何响应(或失败的连接/超时) 。

自述文件中有一些示例准确显示了您要查找的内容:https://www.npmjs.com/package/mockttp#get-testing。在您的特定情况下,我会尝试以下方法:

const makeRequest = require("./your-code");
const mockServer = require("mockttp").getLocal();

describe("makeRequest", () => {
    beforeEach(() => mockServer.start());
    afterEach(() => mockServer.stop());

    it("resolves happily for successful requests", () => {
        await mockServer.get("http://google.com").thenReply(200, "A mocked response");

        // Here: configure your code to use mockServer.url as a proxy

        let result = await makeRequest();

        expect(response).to.equal('Connected to google');
    });

    it("rejects failed requests with an error", () => {
        await mockServer.get("http://google.com").thenReply(500, "An error!");

        // Here: configure your code to use mockServer.url as a proxy

        let result = makeRequest();

        expect(response).to.be.rejectedWith('Failed to connect to Google');
    });
});

配置代理将取决于您使用的库,但是对于http.get https://www.npmjs.com/package/global-tunnel应该做到这一点,或者您可以将{ proxy: mockServer.url }选项直接传递给http.get调用。或者当然,您可以使http://google.com URL可配置,将其设置为mockServer.url,并模拟/而不是http://google.com

主要区别在于这是一个真正的集成测试。您不是在JS级别上进行嘲笑,而是在发送真实的HTTP请求,测试实际发送和接收的内容,以及实际上您的完整Node +应用程序代码将如何对此做出响应。

您可以完全在JS级别上进行模拟,并且测试速度会稍快(大约2毫秒vs每个测试10毫秒),但是很容易获得不准确的结果,并且可以通过测试,但是破坏了真实世界的功能

答案 3 :(得分:1)

以下是使用mochachai进行测试的示例。我们还需要sinon来存根http库。

http://sinonjs.org/releases/v1.17.7/stubs/

// test.js

const chai = require('chai');
const expect = chai.expect;
const sinon = require('sinon');
const http = require('http');

const someRequest = require('./index');

describe('some request test', function() {
  let httpGetStub;

  beforeEach(function() {
    httpGetStub = sinon.stub(http, 'get'); // stub http so we can change the response
  });

  afterEach(function() {
    httpGetStub.restore();
  });

  it('responses with success message', function() {
    httpGetStub.yields({ headers: { 'content-type': 200 }}); // use yields for callback based function like http.get
    return someRequest().then(res => { 
      expect(res).to.equal('Connected to Google');      
    });
  });

  it('rejects with error message', function() {
    httpGetStub.yields({ headers: { 'content-type': 400 }});
    return someRequest().catch(err => { 
      expect(err.message).to.equal('Failed to connect to Google');      
    });
  });
})

希望有帮助。