web-dev-qa-db-fra.com

Exécuter le même test mocha plusieurs fois avec des données différentes

Problème

J'ai plusieurs tests qui font la même chose dans le moka. Pour moi, il s’agit d’une duplication, c’est la pire chose à faire lorsque vous souhaitez que votre système soit maintenable.

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'));
});

De quoi ai-je besoin

J'ai besoin d'un moyen de réduire mes tests de moka dupliqués dans un seul. 

Par exemple, dans PhpUnit (et d'autres frameworks de test), vous avez dataProviders .
Dans phpUnit, un fournisseur de données fonctionne de la manière suivante: 

<?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)
        );
    }
}

Le fournisseur ici injecte des paramètres dans le test et le test exécute tous les cas. Est parfait pour le test dupliqué.

Je veux savoir s'il y a quelque chose de similaire dans le moka, par exemple: 

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(...)}
  };
};

Ce que j'ai déjà regardé

Il existe une technique appelée Comportements partagés . Mais cela ne résout pas le problème directement avec une suite de tests, il résout simplement le problème avec différents composants qui ont des tests dupliqués.

La question

Connaissez-vous un moyen d'implémenter des fournisseurs de données dans le moka?

40
Tomas Prado

Mocha ne fournit pas un outil pour cela, mais il est facile de le faire vous-même. Il vous suffit d'exécuter les tests dans une boucle et de donner les données à la fonction de test à l'aide d'une fermeture:

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));
    });
});
21
Kaizo

Une approche de base pour exécuter le même test avec des données différentes consiste à répéter le test dans une boucle fournissant les données:

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 () {
      ...
    });
  });
});

before s'exécute, eh bien, avant tout its dans describe. Si vous devez utiliser certaines des options de before, ne pas incluez-les dans la boucle forEach car mocha lancera d'abord toutes les befores et toutes les its, ce qui n'est probablement pas souhaité. Vous pouvez soit mettre toute la describe dans la boucle:

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 () {
      ...
    });
  });
});

Si vous ne souhaitez pas polluer vos tests avec plusieurs describes, vous pouvez utiliser le module controversé sinon à cet égard:

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);
      ...
    });
  });
});

Sinon se sent sale mais efficace. Plusieurs modules d'aide tels que leche sont basés sur sinon, mais il est indiscutable d'introduire davantage de complexité.

16
Wtower

Leche ajoute cette fonctionnalité à Mocha. Voir les annonces et docs .

C'est mieux que de simplement passer en revue les tests, car si un test échoue, il vous indique quel fichier a été impliqué.

Mettre à jour:

Je n’ai pas aimé la configuration de Leche et je n’ai pas réussi à la faire fonctionner avec Karma. J’ai donc finalement extrait le fournisseur de données dans un fichier séparé }. 

Si vous voulez l'utiliser, il suffit de _ { saisir le source } _. La documentation est disponible dans le fichier Readme , et vous trouverez des informations supplémentaires et des conseils d'utilisation dans le fichier lui-même.

3
hashchange

Sur la base de la réponse de @ Kaizo, voici ce que j'ai proposé pour mon test (c'est un contrôleur qui extrait certains paramètres de la demande) pour émuler le fournisseur de données dans PHPUnit. La méthode getParameters va recevoir la demande d'Express, puis utiliser req.param pour inspecter certains paramètres de requête, par exemple, GET /jobs/?page=1&per_page=5. Cela montre également comment stub l'objet de requête Express.

J'espère que cela peut aider quelqu'un aussi.

// 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);
            });
        }
    }
};

Le seul inconvénient est que Mocha ne compte pas les assertions réelles (comme le fait PHPUnit), il se présente sous la forme d'un test.

2
Andrew Eddie

J'ai trouvé mocha-testcheck être l'outil le plus simple pour cela. Il génère toutes sortes de données. Cela déterminera quelle entrée est la cause de l'échec de votre test.

0
Zachary Ryan Smith

Une solution plus simple est décrite ci-dessous en utilisant mocha-testdata library.

Exemple de solution au problème.

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);
  });
});

Si vous avez besoin de tester les appels de fonction async qui constituent le cas le plus courant dans l'application node.js, utilisez plutôt 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();
    });
  });
});
0