首页 > 解决方案 > Mocha、Sinon 和 Chai 在回调中测试两个 http 调用

问题描述

我正在用 Chai Mocha 和 Sinon 做一些非常简单的测试。我想知道您将如何测试在回调内部调用的 http 方法。如果可以的话,请指出我正确的方向,努力在上面找到任何东西,我知道你可以做到,只是不知道怎么做。我的代码如下:

index.js

const http = require('http')

class Index {
  add(a, b) {
    return a + b
  }

  get(uri) {
    http.get(uri, () => {
      http.get('/', function() {
        return
      })
    })
  }
}

module.exports = Index

index.spec.js

const Index = require('../index')
const http = require('http')
const { stub, fake, assert } = require('sinon')
const { expect } = require('chai')

let httpSpy;

beforeEach(function () {
  a = new Index()
  httpSpy = stub(http, 'get')
})

describe('When the get method is invoked', function () {
  beforeEach(function () {
    a.get('http://www.google.co.uk')
  })

  it('should make a call to the http service with passed in uri', function () {
    assert.calledOnce(httpSpy) // This really should be called twice (Part I am struggling with)
    assert.calledWith(httpSpy, 'http://www.google.co.uk')
    // I want to test instead that the httpSpy was called twice as, inside the get method, when the first http get resolves, another one gets fired off also
  })
})

标签: node.jsmocha.jssinon

解决方案


有两个问题。

首先,我们无法判断Index.get()方法执行何时结束(它不接受回调,不返回承诺,不标记为异步等)。

get(uri) { ... }

这种方法的使用极其不方便。例如:如果我们想先做Index.get(),然后在我们做不到之后立即做一些动作。

为了解决这个问题,我们可以添加一个回调作为Index.get.

第二个问题是该http.get方法是如何被存根的:

httpSpy = stub(http, 'get')

这一行的基本意思是:http.get用一个空函数替换。如果您在内部传递回调,则该虚拟函数不会抛出,不会调用它。

这就是为什么http.get只调用一次。它只是忽略了http.get第二次应该调用的传递的回调。

为了解决这个问题,我们可以使用stub.yields()方法让 sinon 知道传递给存根的最后一个参数是回调(并且 sinon 需要调用它)。您可以在文档中找到该方法。

这是一个工作示例,请参阅我的评论:

class Index {
    // Added a callback here
    get(uri, callback) {
        http.get(uri, () => {
            http.get('/', () => {
                // Pass any data you want to return here
                callback(null, {});
            })
        })
    }
}

let httpSpy;

beforeEach(() => {
    a = new Index()
    // Now sinon will expect a callback as a last parameter and will call it
    httpSpy = stub(http, 'get').yields();
})

describe('When the get method is invoked', () => {
    const uri = 'http://www.google.co.uk';

    // Now we are waiting for the execution to end before any assertions
    beforeEach(done => {
        a.get(uri, done);
    });

    it('should make a call to the http service with passed in uri', () => {
        assert.calledTwice(httpSpy);
        assert.match(httpSpy.getCall(0).args[0], uri);
        assert.match(httpSpy.getCall(1).args[0], '/');
    });
})


推荐阅读