【问题标题】:Running the same mocha test multiple times with different data使用不同的数据多次运行相同的 mocha 测试
【发布时间】:2013-06-13 05:03:33
【问题描述】:

问题

我有几个测试在 mocha 中做同样的事情。对我来说,这是重复,当您希望您的系统可维护时,这是最糟糕的事情。

var exerciseIsPetitionActive = function (expected, dateNow) {
    var actual = sut.isPetitionActive(dateNow);
    chai.assert.equal(expected, actual);
};

test('test_isPetitionActive_calledWithDateUnderNumSeconds_returnTrue', function () {
    exerciseIsPetitionActive(true, new Date('2013-05-21 13:11:34'));
});

test('test_isPetitionActive_calledWithDateGreaterThanNumSeconds_returnFalse', function () {
    exerciseIsPetitionActive(false, new Date('2013-05-21 13:12:35'));
});

我需要什么

我需要一种将重复的 mocha 测试合并为一个的方法。

例如,在 PhpUnit(和其他测试框架)中,您有 dataProviders
在 phpUnit 中,dataProvider 以这种方式工作:

<?php class DataTest extends PHPUnit_Framework_TestCase {
    /**
     * @dataProvider provider
     */
    public function testAdd($a, $b, $c)
    {
        $this->assertEquals($c, $a + $b);
    }

    public function provider()
    {
        return array(
          array(0, 0, 0),
          array(0, 1, 1),
          array(1, 0, 1),
          array(1, 1, 3)
        );
    }
}

这里的provider给测试注入参数,测试执行所有的case。非常适合重复测试。

我想知道mocha中是否有类似的东西,例如这样的:

var exerciseIsPetitionActive = function (expected, dateNow) {
    var actual = sut.isPetitionActive(dateNow);
    chai.assert.equal(expected, actual);
};

@usesDataProvider myDataProvider
test('test_isPetitionActive_calledWithParams_returnCorrectAnswer', function (expected, date) {
    exerciseIsPetitionActive(expected, date);
});

var myDataProvider = function() {
  return {
      {true, new Date(..)},
      {false, new Date(...)}
  };
};

我已经看过的内容

有一种技术叫做Shared Behaviours。但它并没有直接用测试套件解决问题,它只是解决了具有重复测试的不同组件的问题。

问题

你知道在 mocha 中实现 dataProviders 的任何方法吗?

【问题讨论】:

标签: javascript mocha.js


【解决方案1】:

Mocha 没有为此提供工具,但您自己很容易做到。您只需要在循环中运行测试并使用闭包将数据提供给测试函数:

suite("my test suite", function () {
    var data = ["foo", "bar", "buzz"];
    var testWithData = function (dataItem) {
        return function () {
            console.log(dataItem);
            //Here do your test.
        };
    };

    data.forEach(function (dataItem) {
        test("data_provider test", testWithData(dataItem));
    });
});

【讨论】:

  • 这对我不起作用。我正在使用describe()it() 语法。返回一个测试函数是否重要,或者我们可以直接在testWithData(dataItem) 中进行测试吗?
  • 我必须确保每个额外的 javascript(如 for each)都在 it('tests', function(){ /*HERE */ }) 内。当我使用it()for 循环内的每个测试时,我必须将所有内容都包装在describe(...) 中。我最终得到了一个层次结构describe &gt; it &gt; forEach &gt; describe &gt; it,这使得我循环中的所有测试都出现在我的测试结束时,不管它在测试期间何时发生,但它确实有效。
  • 你能提供it()describe()的要点吗?
  • @Kaizo:这仅对单个它有帮助,假设describe --&gt; it --&gt; forEach --&gt; run asserts here with different values based on forEach loop,但是如何以单个变量的不同值多次运行完整的describe?否则在每个 it 内再次重复 forEach
  • 如果这不是提出这个问题的正确论坛,我深表歉意,但这似乎不起作用,原因如下: - Mocha 似乎没有“套件”或“测试(foo )" 构造 - 使用此 exact 代码不起作用 - 它会因“未定义套件”而出错。如何声称这是答案?版本:节点 6.6.0、Mocha 3.1.2
【解决方案2】:

我发现mocha-testcheck 是最简单的工具。它生成各种数据。它将缩小导致测试失败的输入范围。

【讨论】:

    【解决方案3】:

    下面使用mocha-testdata 库描述了一个更简单的解决方案。

    问题的示例解决方案。

    import * as assert from assert;
    import { givenAsync } from mocha-testdata;
    
    suite('My async test suite', function () {
      given([0, 0, 0], [0, 1, 1], [1, 0, 1], [1, 1, 3]).test('sum to 6', function (a, b, c) {
        assert.strictEqual(a + b + c, 6);
      });
    });
    

    如果您需要测试 node.js 应用程序中最常见的异步函数调用,请改用 givenAsync。

    import * as assert from assert;
    import { givenAsync } from mocha-testdata;
    
    suite('My async test suite', function () {
      givenAsync([1, 2, 3], [3, 2, 1]).test('sum to 6', function (done, a, b, c) {
        doSomethingAsync(function () {
            assert.strictEqual(a + b + c, 6);
            done();
        });
      });
    });
    

    【讨论】:

      【解决方案4】:

      使用不同数据运行相同测试的基本方法是在提供数据的循环中重复测试:

      describe('my tests', function () {
        var runs = [
          {it: 'options1', options: {...}},
          {it: 'options2', options: {...}},
        ];
      
        before(function () {
          ...
        });
      
        runs.forEach(function (run) {
          it('does sth with ' + run.it, function () {
            ...
          });
        });
      });
      

      beforedescribe 中的所有its 之前运行。如果您需要使用before 中的某些选项,请勿将其包含在forEach 循环中,因为mocha 将首先运行所有befores 和所有its,这可能是不想要的。您可以将整个 describe 放入循环中:

      var runs = [
        {it: 'options1', options: {...}},
        {it: 'options2', options: {...}},
      ];
      
      runs.forEach(function (run) {
        describe('my tests with ' + run.it, function () {
          before(function () {
            ...
          });
      
          it('does sth with ' + run.it, function () {
            ...
          });
        });
      });
      

      如果你不想用多个describes 污染你的测试,你可以使用有争议的模块sinon 来解决这个问题:

      var sinon = require('sinon');
      
      describe('my tests', function () {
        var runs = [
          {it: 'options1', options: {...}},
          {it: 'options2', options: {...}},
        ];
      
        // use a stub to return the proper configuration in `beforeEach`
        // otherwise `before` is called all times before all `it` calls
        var stub = sinon.stub();
        runs.forEach(function (run, idx) {
          stub.onCall(idx).returns(run);
        });
      
        beforeEach(function () {
          var run = stub();
          // do something with the particular `run.options`
        });
      
        runs.forEach(function (run, idx) {
          it('does sth with ' + run.it, function () {
            sinon.assert.callCount(stub, idx + 1);
            ...
          });
        });
      });
      

      诗乃感觉很脏但很有效。一些辅助模块(例如 leche)是基于 sinon 的,但可以说没有必要引入进一步的复杂性。

      【讨论】:

      • 除非我使用“// tslint:disable-next-line:mocha-no-side-effect-code”,否则我的 linter 会在 foreach 调用中添加红线
      【解决方案5】:

      Leche 将该功能添加到 Mocha。请参阅announcementdocs

      这比简单地循环测试要好,因为如果测试失败,它会告诉您涉及哪个数据集。

      更新:

      我不喜欢 Leche 的设置,也没有设法让它与 Karma 一起使用,所以最终我将数据提供者提取到了 separate file 中。

      如果您想使用它,只需grab the source。文档可在 in the Leche readme 获得,您可以在文件本身中找到更多信息和使用提示。

      【讨论】:

      • Leche 是最容易使用的一种。感谢您的建议。
      【解决方案6】:

      根据@Kaizo 的回答,这是我为我的测试(它是一个从请求中获取一些参数的控制器)想出的,以模拟 PHPUnit 中的数据提供者。 getParameters 方法将接收来自 Express 的请求,然后使用 req.param 检查一些查询参数,例如,GET /jobs/?page=1&amp;per_page=5。这也显示了如何存根 Express 请求对象。

      希望它也可以帮助某人。

      // Core modules.
      var assert = require('assert');
      
      // Public modules.
      var express = require('express');
      var sinon = require('sinon');
      
      // Local modules.
      var GetJobs = require(__base + '/resources/jobs/controllers/GetJobs');
      
      /**
       * Test suite for the `GetJobs` controller class.
       */
      module.exports = {
          'GetJobs.getParameters': {
              'should parse request parameters for various cases': function () {
                  // Need to stub the request `param` method; see http://expressjs.com/3x/api.html#req.param
                  var stub = sinon.stub(express.request, 'param');
                  var seeds = [
                      // Expected, page, perPage
                      [{limit: 10, skip: 0}],
                      [{limit: 5, skip: 10}, 3, 5]
                  ];
                  var controller = new GetJobs();
      
                  var test = function (expected, page, perPage) {
                      stub.withArgs('page').returns(page);
                      stub.withArgs('per_page').returns(perPage);
      
                      assert.deepEqual(controller.getParameters(express.request), expected);
                  };
      
                  seeds.forEach(function (seed) {
                      test.apply({}, seed);
                  });
              }
          }
      };
      

      唯一的缺点是 Mocha 不计算实际断言(就像 PHPUnit 一样),它只是显示为一个测试。

      【讨论】:

        猜你喜欢
        • 2014-02-19
        • 2010-10-19
        • 1970-01-01
        • 1970-01-01
        • 2014-10-02
        • 2011-01-18
        • 2016-07-20
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多