javascript - Jasmine 2.0 中的异步工作流测试

标签 javascript unit-testing jasmine

我有一个 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 测试工作流程?

最佳答案

我有办法解决你的问题。我构建了一个小型简单的异步测试框架,它与 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%万无一失,但它对我有用。如果您有任何问题,请告诉我。

关于javascript - Jasmine 2.0 中的异步工作流测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24039704/

相关文章:

javascript - 警告 XMLHttpRequest 弹出 ajax

javascript - 停止某些链接(包含特定字符串的 href)工作

Angular 单元测试 - 使用 stub 模拟子组件

asp.net-mvc - 难以启动基本单元测试(我书中的示例——SportsStore)

node.js - Node js中的单元测试/模拟谷歌数据存储

javascript - 使用 Jasmine 模拟 firebase 调用

Angular 2 : jasmin test case for activateRoute. snapshot.queryParams [""]

javascript - react 的默认值在 meteor 中不起作用

javascript - Node.js、g++ (gcc) 和子进程 - gcc 无法打开输出文件错误

oracle - PL/SQL : Execute procedure in another procedure