承诺的 Angular 服务单元测试失败
Unit test failing on Angular service with promises
JSFiddle 显示问题:
http://jsfiddle.net/ggvfwoeL/3/
我有一个 Angular 应用程序,其中包含用于生成威胁的服务。威胁生成是使用 nools 规则引擎完成的。该服务公开了一个函数 generateForElement
,该函数 returns 是一个承诺。该函数如下所示(有关完整详细信息,请参阅 JSFiddle):
var Element = function (element) { this.type = element.attributes.type; }
var Threats = function () { this.collection = []; }
function generateForElement(element) {
var threats = new Threats();
var el = new Element(element);
flow.getSession(threats, el).match();
return $q.when(threats.collection);
}
规则引擎设置为当您使用 tm.Process
类型的元素调用 generateForElement
时,它会生成 2 个威胁。这是定义规则的代码(显然,为了使问题更清楚,这已大大简化):
function initialiseFlow() {
return nools.flow('Element threat generation', function (flow) {
flow.rule('Spoofing Threat Rule', [
[Element, 'el', 'el.type == "tm.Process"'],
[Threats, 'threats']
], function (facts) {
facts.threats.collection.push('Spoofing');
});
flow.rule('Tampering Threat Rule', [
[Element, 'el', 'el.type == "tm.Process"'],
[Threats, 'threats']
], function (facts) {
facts.threats.collection.push('Tampering');
});
});
}
当我在我的应用程序中手动测试它时,我看到生成了 2 个威胁。但是我的单元测试在这条线上失败了
expect(threats.length).toEqual(2);
有错误
Error: Expected 1 to equal 2.
看来只生成了 1 个威胁。单元测试定义是这样的:
describe('threatengine service', function () {
var threatengine;
var $rootScope;
beforeEach(function () {
angular.mock.module('app')
angular.mock.inject(function (_$rootScope_, _threatengine_) {
threatengine = _threatengine_;
$rootScope = _$rootScope_;
});
$rootScope.$apply();
});
describe('threat generation tests', function () {
it('process should generate two threats', function () {
var element = { attributes: { type: 'tm.Process' }};
var threats;
threatengine.generateForElement(element).then(function (data) {
threats = data;
});
expect(threats).toBeUndefined();
$rootScope.$apply();
expect(threats).toBeDefined();
expect(threats.length).toEqual(2);
});
});
});
显然我做错了什么。正如我所说,当我 运行 完整的应用程序时,我肯定会遇到 2 个威胁,这让我认为错误要么出在单元测试上,要么出在我处理服务承诺的方式上,但我就是不能'没看到。
为什么我的单元测试失败了?
第一个问题在flow.getSession(threats, el).match();
调用中,在您的代码中是同步调用的,但最初它似乎是异步的(我对nools 不熟悉,但是here are the docs)。因此,即使您将 console.log
放在两个规则的处理程序中,您也会发现规则的处理方式比您的以下同步代码要晚。它的解决方案是使用承诺,.match()
returns:
function generateForElement(element) {
var threats = new Threats();
var el = new Element(element);
// handle async code via promises and resolve it with custom collection
return flow.getSession(threats, el).match().then(function () {
return threats.collection;
});
}
另一个问题在测试文件中。在那里,你也有异步代码,但你像处理同步代码一样处理它。参见 Asynchronous Support in Jasmine docs。基本上,如果您的测试是异步的,您必须告诉 Jasmine 并在完成时通知它。
it('process should generate two threats', function (done) {
// telling Jasmine that code is async ----------^^^
var element = { attributes: { type: 'tm.Process' }};
// this call is async
threatengine.generateForElement(element).then(function (threats) {
expect(threats).toBeUndefined();
expect(threats).toBeDefined();
expect(threats.length).toEqual(2);
done(); // telling Jasmine that code has completed
});
// is required to start promises cycle if any
$rootScope.$apply();
});
更新:
这是 Jasmine 1.3 的规范,它使用 another API 进行异步流:
it('process should generate two threats', function (done) {
var element = { attributes: { type: 'tm.Process' }};
var threats;
runs(function () {
threatengine.generateForElement(element).then(function (data) {
threats = data;
});
$rootScope.$apply();
});
waitsFor(function () {
return typeof threats !== 'undefined';
});
runs(function () {
expect(threats).not.toBeUndefined();
expect(threats).toBeDefined();
expect(threats.length).toEqual(20);
});
});
JSFiddle 显示问题:
http://jsfiddle.net/ggvfwoeL/3/
我有一个 Angular 应用程序,其中包含用于生成威胁的服务。威胁生成是使用 nools 规则引擎完成的。该服务公开了一个函数 generateForElement
,该函数 returns 是一个承诺。该函数如下所示(有关完整详细信息,请参阅 JSFiddle):
var Element = function (element) { this.type = element.attributes.type; }
var Threats = function () { this.collection = []; }
function generateForElement(element) {
var threats = new Threats();
var el = new Element(element);
flow.getSession(threats, el).match();
return $q.when(threats.collection);
}
规则引擎设置为当您使用 tm.Process
类型的元素调用 generateForElement
时,它会生成 2 个威胁。这是定义规则的代码(显然,为了使问题更清楚,这已大大简化):
function initialiseFlow() {
return nools.flow('Element threat generation', function (flow) {
flow.rule('Spoofing Threat Rule', [
[Element, 'el', 'el.type == "tm.Process"'],
[Threats, 'threats']
], function (facts) {
facts.threats.collection.push('Spoofing');
});
flow.rule('Tampering Threat Rule', [
[Element, 'el', 'el.type == "tm.Process"'],
[Threats, 'threats']
], function (facts) {
facts.threats.collection.push('Tampering');
});
});
}
当我在我的应用程序中手动测试它时,我看到生成了 2 个威胁。但是我的单元测试在这条线上失败了
expect(threats.length).toEqual(2);
有错误
Error: Expected 1 to equal 2.
看来只生成了 1 个威胁。单元测试定义是这样的:
describe('threatengine service', function () {
var threatengine;
var $rootScope;
beforeEach(function () {
angular.mock.module('app')
angular.mock.inject(function (_$rootScope_, _threatengine_) {
threatengine = _threatengine_;
$rootScope = _$rootScope_;
});
$rootScope.$apply();
});
describe('threat generation tests', function () {
it('process should generate two threats', function () {
var element = { attributes: { type: 'tm.Process' }};
var threats;
threatengine.generateForElement(element).then(function (data) {
threats = data;
});
expect(threats).toBeUndefined();
$rootScope.$apply();
expect(threats).toBeDefined();
expect(threats.length).toEqual(2);
});
});
});
显然我做错了什么。正如我所说,当我 运行 完整的应用程序时,我肯定会遇到 2 个威胁,这让我认为错误要么出在单元测试上,要么出在我处理服务承诺的方式上,但我就是不能'没看到。
为什么我的单元测试失败了?
第一个问题在flow.getSession(threats, el).match();
调用中,在您的代码中是同步调用的,但最初它似乎是异步的(我对nools 不熟悉,但是here are the docs)。因此,即使您将 console.log
放在两个规则的处理程序中,您也会发现规则的处理方式比您的以下同步代码要晚。它的解决方案是使用承诺,.match()
returns:
function generateForElement(element) {
var threats = new Threats();
var el = new Element(element);
// handle async code via promises and resolve it with custom collection
return flow.getSession(threats, el).match().then(function () {
return threats.collection;
});
}
另一个问题在测试文件中。在那里,你也有异步代码,但你像处理同步代码一样处理它。参见 Asynchronous Support in Jasmine docs。基本上,如果您的测试是异步的,您必须告诉 Jasmine 并在完成时通知它。
it('process should generate two threats', function (done) {
// telling Jasmine that code is async ----------^^^
var element = { attributes: { type: 'tm.Process' }};
// this call is async
threatengine.generateForElement(element).then(function (threats) {
expect(threats).toBeUndefined();
expect(threats).toBeDefined();
expect(threats.length).toEqual(2);
done(); // telling Jasmine that code has completed
});
// is required to start promises cycle if any
$rootScope.$apply();
});
更新:
这是 Jasmine 1.3 的规范,它使用 another API 进行异步流:
it('process should generate two threats', function (done) {
var element = { attributes: { type: 'tm.Process' }};
var threats;
runs(function () {
threatengine.generateForElement(element).then(function (data) {
threats = data;
});
$rootScope.$apply();
});
waitsFor(function () {
return typeof threats !== 'undefined';
});
runs(function () {
expect(threats).not.toBeUndefined();
expect(threats).toBeDefined();
expect(threats.length).toEqual(20);
});
});