【问题标题】:How to test if a method is being called in the controller?如何测试控制器中是否正在调用方法?
【发布时间】:2014-11-14 09:36:23
【问题描述】:

这是一个关于在 Sails JS 中使用 Mocha 进行异步测试的问题。

我正在使用 supertest 库在 Sails JS 中编写控制器测试。我想检查一个方法是否在 HTTP POST 上被调用到我们的控制器。为此,我对该方法进行存根,并希望在end() 中调用它,如下所示:

request(sails.hooks.http.app)
    .post('heartbeat/create')
    .send('device: 1')
    .end(function(err, res) {
        expect(publishCreateStub.called).to.be.true;
        done();
    });

当我运行它时,期望失败,因为断言时没有调用该方法。但是当我将期望放在setTimeout 中时,它会起作用:

request(sails.hooks.http.app)
    .post('heartbeat/create')
    .send('device: 1')
    .end(function(err, res) {
        setTimeOut(function() {
            expect(publishCreateStub.called).to.be.true;
            done();
        }, 1000);
    });

如果没有setTimeout,有什么方法可以使测试通过?

这是我正在测试的代码部分:HeartbeatController#create

您也可以通过发送拉取请求帮助我们解决问题:https://github.com/multunus/one-mdm/issues/1

【问题讨论】:

  • publishCreateStub 是什么?
  • publishCreate 是一种在创建模型时通过套接字广播消息的方法。可以看代码部分here

标签: javascript node.js sails.js mocha.js supertest


【解决方案1】:

实际的问题是你的控制器不等待publishCreate 被调用。因此,您在回复 201 Created 时并未检查是否已创建任何内容。

Heartbeat.findOneHeartbeat.publishCreate 有可能会失败,但你会知道的。

要解决此问题,您应该修改控制器,将响应部分移动到 Heartbeat 承诺回调中:

create: function (req, res, next) {
  if(req.isSocket) {
    Heartbeat.watch(req);
  }

  Heartbeat.create(req.body)
    .exec(function(error, heartbeat) {
      if(error) {
        res.status(422);
        return res.send('Invalid heartbeat data');
      }

      Heartbeat.findOne(heartbeat.id).populate('device').then(function(newHeartbeat) {
        Heartbeat.publishCreate(newHeartbeat.device);
      }).then(function() {
        // success
        res.status(201);
        res.json({
          device: heartbeat.device
        });
      }, function(err) {
        // something bad happened
        next(err);
       });

    });
}

在我的示例中,我将实际错误处理委托给下一个快速错误处理中间件:

next(err);

但您可以决定自己处理错误,例如:

res.status(400);
res.send('Can not publish Heartbeat create');

【讨论】:

    【解决方案2】:

    您的问题不在您的测试中,而在您的应用程序中:

    Heartbeat.create(req.body)
      .exec(function(error, heartbeat) {
        if(error) {
          res.status(422);
          return res.send('Invalid heartbeat data');
        }        
    
        Heartbeat.findOne(heartbeat.id).populate('device').then(function(newHeartbeat) {
          Heartbeat.publishCreate(newHeartbeat.device);
        });
    
        res.status(201);
        return res.json({
          device: heartbeat.device
        });
      });
    }
    

    https://github.com/multunus/one-mdm/blob/master/api/controllers/HeartbeatController.js

    在这里,您创建了一个承诺,但没有返回该承诺。因此,在继续之前,您无法等待承诺被履行(或错误)。

    在不详细了解您的应用程序的情况下,我不确定解决方法。我会考虑将中间的位抽象为服务*,然后您的 HeartbeatController 测试可以检查服务是否被(同步)调用,并且您对服务的测试可以执行使用承诺的异步部分。

    * 更新:“服务”是指将其从 HTTP 的任何关注点中抽象出来,因此它不知道请求或响应对象。最终结果是您已将 HTTP 部分(控制器)与管理 Heartbeat 对象的部分分开。这使得这两个部分更容易进行单元测试。

    【讨论】:

      猜你喜欢
      • 2014-01-03
      • 1970-01-01
      • 1970-01-01
      • 2016-01-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-12
      • 2014-07-20
      相关资源
      最近更新 更多