簡體   English   中英

如何對一個調用另一個返回promise的函數進行單元測試?

[英]How to unit test a function which calls another that returns a promise?

我有一個使用express 4的node.js應用程序,這是我的控制器:

var service = require('./category.service');

module.exports = {
  findAll: (request, response) => {
    service.findAll().then((categories) => {
      response.status(200).send(categories);
    }, (error) => {
      response.status(error.statusCode || 500).json(error);
    });
  }
};

它調用我的服務,它返回一個promise。 一切正常但我在嘗試進行單元測試時遇到了麻煩。

基本上,我想確保根據我的服務返回的內容,我使用正確的狀態代碼和正文刷新響應。

因此,對於mocha和sinon,它看起來像:

it('Should call service to find all the categories', (done) => {
    // Arrange
    var expectedCategories = ['foo', 'bar'];

    var findAllStub = sandbox.stub(service, 'findAll');
    findAllStub.resolves(expectedCategories);

    var response = {
       status: () => { return response; },
       send: () => {}
    };
    sandbox.spy(response, 'status');
    sandbox.spy(response, 'send');

    // Act
    controller.findAll({}, response);

    // Assert
    expect(findAllStub.called).to.be.ok;
    expect(findAllStub.callCount).to.equal(1);
    expect(response.status).to.be.calledWith(200); // not working
    expect(response.send).to.be.called; // not working
    done();
});

當我測試的函數返回一個promise時,我已經測試了我的類似場景,因為我可以在當時掛鈎我的斷言。

我也試圖用一個Promise包裝controller.findAll並從response.send解析它,但它既不起作用。

您應該將assert部分移動到res.send方法中,以確保在斷言之前完成所有異步任務:

var response = {
   status: () => { return response; },
   send: () => {
     try {
       // Assert
       expect(findAllStub.called).to.be.ok;
       expect(findAllStub.callCount).to.equal(1);
       expect(response.status).to.be.calledWith(200); // not working
       // expect(response.send).to.be.called; // not needed anymore
       done();
     } catch (err) {
       done(err);
     }
   },
};

這里的想法是讓service.findAll()返回的承諾可以在測試代碼中返回,而無需調用service 據我sinon-as-promised ,你可能使用的sinon-as-promised不允許這樣做。 所以我只使用了原生的Promise (希望你的節點版本不是太老了)。

const aPromise = Promise.resolve(expectedCategories); 
var findAllStub = sandbox.stub(service, 'findAll');
findAllStub.returns(aPromise);

// response = { .... }

controller.findAll({}, response);

aPromise.then(() => {
    expect(response.status).to.be.calledWith(200);
    expect(response.send).to.be.called;    
});

當代碼難以測試時,它可以表明可以有不同的設計可能性來探索,這促進了簡單的測試。 跳出來的是service包含在您的模塊中,並且依賴性根本不會公開。 我覺得目標不應該是找到一種方法來測試你的代碼,而是找到一個優化的設計。

IMO目標是找到一種公開service的方法,以便您的測試可以提供存根實現,以便可以隔離地同步測試findAll的邏輯。

一種方法是使用像mockeryrewire的庫。 兩者都相當容易使用(根據我的經驗,隨着測試套件和模塊數量的增長,嘲諷開始降級並變得非常難以維護)它們將允許您修補var service = require('./category.service'); 通過提供自己的服務對象,並定義自己的findAll

另一種方法是重新構建代碼,以某種方式將service公開給調用者。 這將允許您的調用者(單元測試)提供自己的service存根。

一種簡單的方法是導出函數contstructor而不是對象。

module.exports = (userService) => {

  // default to the required service
  this.service = userService || service;

  this.findAll = (request, response) => {
    this.service.findAll().then((categories) => {
      response.status(200).send(categories);
    }, (error) => {
      response.status(error.statusCode || 500).json(error);
    });
  }
};

var ServiceConstructor = require('yourmodule');
var service = new ServiceConstructor();

現在,測試可以為service創建存根,並將其提供給ServiceConstructor以運行findAll方法。 完全不需要異步測試。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM