我有一个AngularJS应用程序,我需要测试工作流程并保证在广播事件后设置正确的值。
在1.3中我会这样做:
it('should have the correct match workflow', function() {
// matchMaking event
runs(function() {
scope.$broadcast('matchMaking', gameId);
});
waitsFor(function() {
return (scope.match && scope.match.game);
}, 'A game should be defined', 3000);
runs(function() {
expect(scope.match.game).toBeDefined();
});
// matchCreate event
runs(function() {
scope.$broadcast('matchCreate', gameId, {}, {});
});
waitsFor(function() {
return scope.match.status === 'CREATED';
}, 'Match status should be \'CREATED\'', 3000);
runs(function() {
expect(scope.match.id).toBeDefined();
expect(scope.match.player).toBeDefined();
expect(scope.match.opponent).toBeDefined();
});
// matchPrepare event
runs(function() {
scope.$broadcast('matchPrepare');
});
waitsFor(function() {
return scope.match.status === 'PREPARED';
}, 'Match status should be \'PREPARED\'', 3000);
runs(function() {
expect(scope.match.id).toBeDefined();
});
// ... continues
});
使用Jasmine 2.0,似乎测试工作流的唯一解决方案是将setTimeout
函数链接到彼此内(所有期望必须在同一规范内才能使用相同的范围):
beforeEach(inject(function($rootScope, $compile) {
jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000;
scope = $rootScope;
element = angular.element('<pg-match-making></pg-match-making>');
$compile(element)($rootScope);
$rootScope.$digest();
}));
it('should have the correct match workflow', function(done) {
var timeoutTick = 100;
scope.$broadcast('matchMaking', gameId);
setTimeout(function(){
expect(scope.match.game).toBeDefined();
scope.$broadcast('matchCreate', gameId, {}, {});
setTimeout(function(){
expect(scope.match.status).toEqual('CREATED');
expect(scope.match.id).toBeDefined();
expect(scope.match.player).toBeDefined();
expect(scope.match.opponent).toBeDefined();
scope.$broadcast('matchPrepare');
setTimeout(function(){
expect(scope.match.status).toEqual('PREPARED');
expect(scope.match.id).toBeDefined();
// ... call done() on the last setTimeout()
}, timeoutTick);
}, timeoutTick);
}, 6000);
});
我最终得到了一堆7 setTimeout
,这使得源代码更难阅读,测试速度非常慢。
使用Jasmine 2.0测试工作流程有没有更好的方法?
答案 0 :(得分:1)
我有一个解决您问题的方法。我已经构建了一个小的简单异步测试框架,可以很好地与Jasmine 2.x一起使用,但它使用jQuery Deferred对象来安排延续。
function asyncWait(delay) {
return new $.Deferred(function () {
var _self = this;
setTimeout(function () {
_self.resolve();
}, delay || 0);
}).promise();
}
var Async = function(init) {
var d = new $.Deferred(init);
this.promise = d.promise();
d.resolve();
};
Async.prototype.continueWith = function (continuation, delay) {
var _self = this;
_self.promise.then(function () {
_self.promise = asyncWait(delay).then(continuation);
});
return _self;
};
Async.prototype.waitsFor = function (condition, timeout, pollInterval) {
pollInterval = pollInterval || 10;
timeout = timeout || 5000;
var _self = this,
wait_d = new $.Deferred(),
t = 0,
ln = function () {
if (condition()) {
wait_d.resolve();
return;
}
if (t >= timeout) {
wait_d.reject();
throw "timeout was reached during waitsFor";
}
t += pollInterval;
setTimeout(ln, pollInterval);
};
_self.promise.then(ln);
_self.promise = wait_d.promise();
return _self;
};
要使用此代码,请连接Jasmine测试并使用Async类的新实例
it("some async test workflow I want to run", function (done) {
new Async(function () {
//wire up the first async call here
var timeoutTick = 100;
scope.$broadcast('matchMaking', gameId);
}).continueWith(function () {
expect(scope.match.game).toBeDefined();
scope.$broadcast('matchCreate', gameId, {}, {})
}, 6000).continueWith(function () {
//more stuff here
}).waitsFor(function () {
// a latch function with timeout - maybe wait for DOM update or something
return $(".my-statefull-element").val() === "updated";
}, 1000).continueWith(done); //finish by waiting for done to be called
});
此代码不是100%傻瓜证明,但它适用于我。如果您有任何问题,请告诉我。
答案 1 :(得分:1)
使用一些额外的javascript,你可以使茉莉花的行为与1.3.1相似,而且你不需要引入任何额外的库。您只需要实现您缺少的轮询功能。这是一个简化的例子:
var value1 = false;
var value2 = false;
var value3 = false;
var test1 = function _test1() {
setTimeout( function() { value1 = true; }, 1000 );
}
var test2 = function _test2() {
setTimeout( function() { value2 = true; }, 5000 );
}
var test3 = function _test3() {
setTimeout( function() { value3 = true; }, 300000 );
}
var asyncCheckFn = function( done, waitFor, verify ) {
if ( waitFor() ) {
verify();
done();
} else {
console.log( 'checking...' );
setTimeout( function() { asyncCheckFn(done, waitFor, verify) }, 500);
}
};
describe('async test suite', function() {
it( 'works with short test', function( done ) {
test1();
asyncCheckFn( done, function() {
return value1;
}, function() {
expect( value1 ).toBe( true );
});
}, 3000 );
it( 'longer delay', function( done ) {
test2();
asyncCheckFn( done, function() {
return value2;
}, function() {
expect( value2 ).toBe( true );
});
}, 10000 );
it( 'fails', function( done ) {
test3();
asyncCheckFn( done, function() {
return value3;
}, function() {
expect( value3 ).toBe( true );
});
}, 3000 );
});
asyncTestFn()执行与waitsFor()函数相同的任务 - 测试条件直到它为真。测试的总超时由传递给it()函数的最后一个参数控制。这是您的示例重写为线性测试而不是嵌套的setTimeouts:
describe('should have the correct match workflow', function() {
var timerTick = 100;
// matchMaking event
it('defines the game', function(done) {
scope.$broadcast('matchMaking', gameId);
asyncCheckFn(done, function() {
return scope.match && scope.match.game;
}, function() {
expect(scope.match.game).toBeDefined();
});
}, 6000);
it('creates the match', function(done) {
scope.$broadcast('matchCreate', gameId, {}, {});
asyncCheckFn(done, function() {
return scope.match.status === 'CREATED';
}, function() {
expect(scope.match.id).toBeDefined();
expect(scope.match.player).toBeDefined();
expect(scope.match.opponent).toBeDefined();
});
}, timerTick);
it('prepares the match', function(done) {
scope.$broadcast('matchPrepare');
asyncCheckFn(done, function() {
return scope.match.status === 'PREPARED';
}, function() {
expect(scope.match.id).toBeDefined();
});
}, timerTick);
// ... continues
});
希望这会有所帮助。
(我知道这有点旧,但我在尝试解决类似问题时遇到了问题 - 如何嵌套顺序,依赖测试(答案,你不能......)。
(使用Jasmine 2.2测试的样品)
答案 2 :(得分:0)