超级测试:检查在res.send()之后发生的事情

时间:2018-11-18 21:04:46

标签: node.js express jestjs restify supertest

我正在执行POST来创建项目并将新创建的项目作为响应发送回客户端。

async (req, res, next) => {
  const item = await createItem(xx, yy, zz);
  res.send(201, item);
}

现在,我还想在创建商品后发出通知,而且还希望在响应客户之后发出通知,以使请求尽快完成。

async (req, res, next) => {
  const item = await createItem(xx, yy, zz);
  res.send(201, item);

  sendNotification(item);
}

如果我想使用jest + supertest进行测试,则外观如下:

test('return 201', () => {
  const app = require('./');
  return request(app)
    .post('/api/items')
    .send({})
    .expect(201)
    .then(response => {
      // test something more
    });
}

但是如何测试sendNotification()是否被调用?

3 个答案:

答案 0 :(得分:0)

您可以在Jest中使用mocking来监视sendNotification()函数并断言该函数已被调用。一个简单的例子:

const sendNotification = require('./sendNotification');
const sendNotificationSpy = jest.spyOn(sendNotification);

test('return 201', () => {
  const app = require('./');
  return request(app)
    .post('/api/items')
    .send({})
    .expect(201)
    .then(response => {
      // test something more
      expect(sendNotificationSpy).toHaveBeenCalled();
    });
}

答案 1 :(得分:0)

调用res.send()后,程序将调用 someService.method({param1}) 函数。

使用sinon监视该服务方法:

it('test after send', function(done){
  const spy = sinon.spy(someService, 'method');
  agent
    .set('Authorization', token)
    .post('/foo')
    .expect(200)
    .then(() => {
      return setTimeout(function() {
        // Assert the method was called once
        sinon.assert.callCount(spy, 1);
        // Assert the method was called with '{param1}' parameter
        sinon.assert.calledWith(spy, {param1});
        // Test callback!
        done();
      }, 100);
    });
});

-将 setTimeout 与最小时间(ms)一起使用,以等待方法被调用。

建议和改进将不胜感激! (我仍在尝试避免使用任意数量的超时时间)

答案 2 :(得分:0)

好吧,虽然不完美,但现在可以正常工作:

我在异步请求处理程序的末尾从另一个包中添加了对外部方法的调用。我知道您不应该仅出于测试目的添加代码,但我更喜欢在测试中随机添加setTimeouts

hooks.js

const deferreds = [];

exports.hookIntoEnd = () => {
  const p = new Promise((resolve, reject) => {
    deferreds.push({ resolve, reject });
  });
  return p;
};

exports.triggerEndHook = () => {
  if (Array.isArray(deferreds)) {
    deferreds.forEach(d => d.resolve());
  }
};

handler.js

const { triggerEndHook } = require('./hooks');

async (req, res, next) => {
  const item = await createItem(xx, yy, zz);
  res.send(201, item);

  sendNotification(item);

  // this is only here so that we can hook into here from our tests
  triggerEndHook();
}

test.js

test('run + test stuff after res.send', async () => {
  const server = require('../index');
  const { hookIntoEnd } = require('../hooks');
  const aws = require('../utils/aws');

  const getObjectMetadataSpy = jest
    .spyOn(aws, 'getObjectMetadata')
    .mockImplementation(() => Promise.resolve({ Metadata: { a: 'b' } }));

  const p = hookIntoEnd();

  const response = await request(server)
    .post('/api/items')
    .send({ foo: 'bar' })
    .set('Accept', 'application/json')
    .expect('Content-Type', /json/)
    .expect(201);

  expect(response.body).toEqual({ id: 1, name: 'test item'});

  // test for code that was run after res.send
  return p.then(async () => {
    console.log('>>>>>>>>>>> triggerEndHook');
    expect(getObjectMetadataSpy).toHaveBeenCalledTimes(2);
  });
});