在测试执行之前从模块加载数据

时间:2015-12-11 20:18:13

标签: javascript node.js asynchronous jasmine protractor

(我最近问this question并接受了答案,但它仍然不是我需要的。)我真的需要根据从模块加载的数据创建动态测试。数组中的每个项目都有自己的describe语句,并带有某些量角器操作。我之前的帖子有一个答案,说要使用it声明,但我不能这样做,因为有太多的事情发生了。

我的主要问题是数据没有及时加载describe。我有另一个建议使用VCR.js或类似的东西,但我不认为那些会起作用,因为我使用的是模块。有没有办法可以将数据保存到单独的文件并加载?这会是一个好方法吗?

var data = require('get-data'); //custom module here

describe('Test', function() {
  var itemsArr;
  beforeAll(function(done) { 
    data.get(function(err, result) {
      itemsArr = result; //load data from module
      done();
    });
  })

  //error: Cannot read property 'forEach' of undefined
  describe('check each item', function() {
    itemsArr.forEach(function(item) {
      checkItem(item);
    });
  });

  function checkItem (item) {
    var itemName = item.name;
    describe(itemName, function() {
      console.log('describe');
      it('should work', function() {
        console.log('it');
        expect(true).toBeTruthy();
      });
    });
  }

});

更新

我使用了Eugene的答案并提出了这个问题。由于it语句没有触发,我无法测试每个项目的研究方式。这个问题是否可以解决?

describe('check each item', function () {
   it('should load data', function (done) {
      browser.wait(itemsPromise, 5000);
     itemsPromise.then(function(itemsArr) {
       expect(itemsArr).toBeTruthy();
       studyArr = itemsArr.filter(function (item) {
         return item.enabled && _.contains(item.tags, 'study');
       });
       studyCount = studyArr.length;
       expect(studies.count()).toEqual(studyCount);
       checkItems(studyArr);
       done();
     });
   });
   function checkItems (itemsArr) {
     itemsArr.forEach(function (item) {
       describe(item.id, function () {
        console.log('checkItems', item.id);
        // doesn't work
         it('should work', function (done) {
           expect(false).toBeTruthy();
           done();
         });
       });
     });
   }
 });

3 个答案:

答案 0 :(得分:3)

您正在尝试执行Jasmine不允许的操作:在测试套件启动后生成测试。有关Jasmine的问题,请参阅this comment

  

一旦套件开始运行,Jasmine不支持添加规范。通常,当我需要这样做时,我已经能够提前知道选项列表并且只是循环通过它们来进行调用。 [...]

("添加规格" ==="添加测试")

关键是你可以动态生成测试,但之前测试套件已开始执行测试。其中一个推论是测试生成不能是异步的。

您的第二次尝试不起作用,因为它正在尝试将测试添加到已经运行的套件中。

您的第一次尝试更接近您的需求,但它不起作用,因为describe立即调用其回调,因此beforeAlldescribe尝试生成时尚未运行测试。

解决方案

这一切都归结为在测试套件开始执行测试之前计算itemsArr 的值。

您可以创建一个同步返回结果的.getSync方法。您的代码将类似于:

var data = require('get-data'); //custom module here
var itemsArr = data.getSync();

describe('Test', function() {
  describe('check each item', function() {
    itemsArr.forEach(function(item) {
      checkItem(item);
    });
  });

[...]

如果无法编写.getSync函数,则可以让外部进程负责生成JSON输出,然后将其反序列化为itemsArr。您可以使用child_process...Sync个功能之一执行此外部流程。

以下是第二个选项如何运作的示例。我使用以下代码创建了一个get-data.js文件,该代码使用setTimeout来模拟异步操作:

var Promise = require("bluebird"); // Bluebird is a promise library.

var get = exports.get = function () {
    return new Promise(function (resolve, reject) {
        var itemsArr = [
            {
                name: "one",
                param: "2"
            },
            {
                name: "two",
                param: "2"
            }
        ];
        setTimeout(function () {
            resolve(itemsArr);
        }, 1000);
    });
};

// This is what we run when were are running this module as a "script" instead
// of a "module".
function main() {
    get().then(function (itemsArr) {
        console.log(JSON.stringify(itemsArr));
    });
};

// Check whether we are a script or a module...
if (require.main === module) {
    main();
}

然后,在spec文件中:

var child_process = require('child_process');

var itemsArr = JSON.parse(child_process.execFileSync(
    "/usr/bin/node", ["get-data.js"]));

describe('Test', function() {
    itemsArr.forEach(function(item) {
        checkItem(item);
    });

    function checkItem (item) {
        var itemName = item.name;
        describe(itemName, function() {
            console.log('describe');
            it('should work', function() {
                console.log('it');
                expect(true).toBeTruthy();
            });
        });
    }
});

我使用jasmine-node测试了上面的代码。以下文件结构:

.
├── data.js
├── get-data.js
└── test
    └── foo.spec.js

./node_modules中包含bluebirdjasmine-node。这就是我得到的:

$ ./node_modules/.bin/jasmine-node --verbose test 
describe
describe
it
it

Test - 5 ms

    one - 4 ms
        should work - 4 ms

    two - 1 ms
        should work - 1 ms

Finished in 0.007 seconds
2 tests, 2 assertions, 0 failures, 0 skipped

答案 1 :(得分:0)

尝试使用承诺,例如:

  var deferred = protractor.promise.defer();
  var itemsPromise = deferred.promise;
  beforeAll(function() { 
    data.get(function(err, result) {
      deferred.fulfill(result);
    });
  })

然后:

  describe('check each item', function() {
    itemsPromise.then(function(itemsArr) {
        itemsArr.forEach(function(item) {
          checkItem(item);
        });
    });
  });

我能想到的另一个解决方案是使用browser.wait等待itemsArr变为空。

答案 2 :(得分:0)

你的get-data模块是否正在使用量角器做一些浏览器?如果是这样,您将需要在controlFlow的上下文中设置/获取itemsArr。否则,它将读取get-data模块中的所有代码,但是推迟执行,而不是等到它完成,然后再向前移动到那些期望语句。

var data = require('get-data'); //custom module here
var itemsArr;

describe('Test', function() {
  beforeAll(function() {
    // hook into the controlFlow and set the value of the variable
    browser.controlFlow().execute(function() {
      data.get(function(err, result) {
        itemsArr = result; //load data from module
      });
    });
  });

  //error: Cannot read property 'forEach' of undefined
  describe('check each item', function() {
    // hook into the controlFlow and get the value of the variable (at that point in time)
    browser.controlFlow().execute(function() {
      itemsArr.forEach(function(item) {
        checkItem(item);
      });
    });
  });

  function checkItem (item) {
    var itemName = item.name;
    describe(itemName, function() {
      console.log('describe');
      it('should work', function() {
        console.log('it');
        expect(true).toBeTruthy();
      });
    });
  }

});