【问题标题】:How can I unit test a complex promise chain?如何对复杂的承诺链进行单元测试?
【发布时间】:2016-09-26 09:30:42
【问题描述】:

我有一些 JavaScript 代码:

var findLeastUsedPassage;

findLeastUsedPassage = function(StudentId) {
  var passageCounts;
  passageCounts = [];
  return db.Passage.findAll({
    where: {
      active: true
    }
  }).each(function(dbPassage) {
    var passage;
    passage = dbPassage.get();
    passage.count = 0;
    return passageCounts.push(passage);
  }).then(function() {
    return db.Workbook.findAll({
      where: {
        SubjectId: 1,
        gradedAt: {
          $ne: null
        },
        StudentId: StudentId
      },
      include: [
        {
          model: db.WorkbookQuestion,
          include: [db.Question]
        }
      ],
      limit: 10,
      order: [['gradedAt', 'DESC']]
    });
  }).each(function(dbWorkbook) {
    return Promise.resolve(dbWorkbook.WorkbookQuestions).each(function(dbWorkbookQuestion) {
      var passageIndex;
      passageIndex = _.findIndex(passageCounts, function(passageCount) {
        return passageCount.id === dbWorkbookQuestion.Question.PassageId;
      });
      if (passageIndex !== -1) {
        return passageCounts[passageIndex].count++;
      }
    });
  }).then(function() {
    passageCounts = _.sortBy(passageCounts, 'count');
    return passageCounts;
  });
};

我想对它进行单元测试(我认为)。我使用mocha 进行测试,但我的测试似乎并不那么全面:

describe('Finding the least used Passage', function() {
  it('should have a function called findLeastUsedPassage', function() {
    return expect(WorkbookLib.findLeastUsedPassage).to.exist;
  });
  return it('should return the least used passages for a student', function() {
    return WorkbookLib.findLeastUsedPassage(10).then(function(passageCounts) {
      var passageCountsLength;
      passageCountsLength = passageCounts.length;
      expect(passageCountsLength).to.equal(74);
      expect(passageCounts[0].count).to.be.at.most(passageCounts[1].count);
      expect(passageCounts[1].count).to.be.at.most(passageCounts[5].count);
      expect(passageCounts[56].count).to.be.at.most(passageCounts[70].count);
      return expect(passageCounts[70].count).to.be.at.most(passageCounts[73].count);
    });
  });
});

单元测试这样的正确方法是什么?

【问题讨论】:

    标签: javascript unit-testing promise mocha.js


    【解决方案1】:

    This 是了解如何分解代码以进行测试的绝佳资源。

    目前,您的代码无法很好地测试,因为逻辑都混合在多个数据库调用、业务逻辑和粘合代码之间。你需要做的是把它分解成多个命名函数,每个 do one thing,就像你现在做的那样。期望不要在链中创建函数,而是应该在链外创建它们,然后在 Promise 链中调用它们。

    var passageCounts = [];
    
    
    function findAllActivePassages() {
      passageCounts = [];
    
      return db.Passage.findAll({
        where: {
          active: true
        }
      })
    }
    
    function countPassages(dbPassage) {
      var passage;
      passage = dbPassage.get();
      passage.count = 0;
      return passageCounts.push(passage);
    }
    
    function findAllSubjects(StudentId) {
      return db.Workbook.findAll({
        where: {
          SubjectId: 1,
          gradedAt: {
            $ne: null
          },
          StudentId: StudentId
        },
        include: [
          {
            model: db.WorkbookQuestion,
            include: [db.Question]
          }
        ],
        limit: 10,
        order: [['gradedAt', 'DESC']]
      });
    }) 
    
    // ...
    
    findAllActivePassages()
      .each(countPassages)
      .then(function() {
        return findAllSubjects(studentId)
      })
      // ...
    

    现在您可以单独和隔离地测试每个功能,以确保它们符合您的预期

    【讨论】:

    • 我将如何有效地测试 findAllActivePassages?
    • 由于 findAllActivePassages 只是一个数据库调用,您可能不需要对它进行单元测试,只要您相信db.Passage.findAll 会正确返回(您不需要单元测试的第三方代码,只有你唯一的代码)
    • 那么,在我的代码中,我想测试什么?
    • 每当您进行任何数据操作、业务逻辑或粘合代码时。您还可以在您使用数据库调用中的任何参数(如 studentId)时测试它是否正确使用它。这实际上取决于您想要做到的彻底程度。至少你应该测试你的 Promise 链中的最后两个函数,你查看从数据库调用返回的数据,做你期望它们做的事情。
    【解决方案2】:

    因此,对于初学者来说,您可能希望分解您的承诺链,以使代码的离散单元更加明显。我做了一些快速的伪javascript(最熟悉的带有节点的,如果这不适合香草javascript,请道歉)。

    var p1 = db.Passage.findAll({ where: { active: true }})
    var p2 = db.Workbook.findAll({
          where: {
            SubjectId: 1,
            gradedAt: {
              $ne: null
            },
            StudentId: StudentId
          },
          include: [
            {
              model: db.WorkbookQuestion,
              include: [db.Question]
            }
          ],
          limit: 10,
          order: [['gradedAt', 'DESC']]
        });
    
    Promise.all([p1, p2])
    .then(function(results){
        var passages = results[0]
        var workbooks = results[1];
    
        var passageCounts = {};
        passages.foreach(function(passage){
            passagecounts[passage.get().id] = 0
        });
    
        workbooks.foreach(function(workbook){
            workbook.workBookQuestions.foreach(function(question){
                return passageCounts[dbWorkbookQuestion.Question.PassageId] += 1;
            })
        });
    
        return Promise.resolve(passageCounts)
    }).then(function(passageCounts){
        passageCounts = _.sortBy(passageCounts, 'count'); //this has to change but don't know what underscore offers for sorting an object used as a hashmap
        return passageCounts;
    });
    

    现在就单元测试而言 - 您正在寻找测试它的离散单元,因此以下用例似乎是合理的:

    • 我会得到预期的结果吗?
    • 如果我给它特定的值,它们是否按照我期望的方式排序?
    • 如果我没有任何查询的结果,它会中断吗?应该吗?

    您可能应该将数据库调用从逻辑中分离出来并将结果传递给一个方法,从而使测试某些场景更容易一些。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-06-02
      • 2013-12-23
      • 1970-01-01
      • 1970-01-01
      • 2016-10-25
      • 2021-09-07
      • 2010-09-11
      • 2016-05-01
      相关资源
      最近更新 更多