【问题标题】:Sleep in Jasmine睡在茉莉花里
【发布时间】:2018-02-22 05:23:02
【问题描述】:

用例: 对于我的 angularJS(1.6.4) 应用程序,我正在用 jasmine 编写单元测试。

在我的测试用例中,我调用了一个 poll() 函数,该函数使用 $interval 反复调用另一个函数 CheckTaskStatus()。我正在监视 CheckTaskStatus() 并想检查它是否已被调用了一定次数。 所以在调用 poll() 之后,我希望能够等待一段时间,然后检查使用 expect() 调用 CheckTaskStatus() 的次数。

问题: 我一直无法找到一种方法让茉莉花在调用 poll() 之后和 expect() 之前等待。

在 poll() 之后,我尝试使用以下选项,但这些选项不会导致 jasmine 睡眠:

  • $超时
  • 设置超时
  • 异步函数在函数返回承诺时调用等待。 服务 从“角度”导入*作为角度;

导出类轮询{ 公共静态 $inject = ['$q', '$http', '$interval'];

constructor(private $q: ng.IQService, private $http, private $interval) {}

public poll(interval: number, pollFn: () => ng.IPromise<any>, until?: (any) => boolean, cancel?: ng.IPromise<any>) {
    let intervalPromise = null;
    const done = this.$q.defer();

    const intervalFn = () => {
        pollFn().then((pollFnResult) => {
            if (until && until(pollFnResult)) {
                this.$interval.cancel(intervalPromise);
                done.resolve();
            }
        }).catch(() => {});  
    };

    // Set up an interval to execute the pollFunction
    intervalPromise = this.$interval(intervalFn, interval);
    intervalPromise.catch(() => {});

    intervalFn();

    if (cancel) {
        cancel.then(() => {
            this.$interval.cancel(intervalPromise);
            done.resolve();
        })
            .catch(() => {}); 
    }

    return done.promise;
}

}

导出默认 angular.module('myPolling', []) .service('polling', Polling).name;

茉莉花测试

fdescribe('myPolling module tests', () => {
    'use strict';
    let $rootScope,
        $q: ng.IQService,
        $http,
        $interval,
        $timeout;

    beforeEach(mockModule('myPolling'));
    beforeEach(() => {
        jasmine.clock().install();
    });
    beforeEach(inject((_$rootScope_, _$q_, _$http_, _$interval_, _polling_, _$timeout_) => {
        this.$rootScope = _$rootScope_;
        this.$q = _$q_;
        this.$http = _$http_;
        this.$interval = _$interval_;
        this.polling = _polling_;
        $timeout = _$timeout_;


    }));

    function sleep(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }

    async function SleepForASecond() {
        await sleep(1050);
    }

    it('blah', () => {

        let pollCount = 0;
        let pollObj = {
            pollFn: () => {
                pollCount++;
                return this.$q.when(pollCount);
            }
        };
         // jasmine.createSpy('pollFn');
        spyOn(pollObj, 'pollFn').and.callThrough();

        let cancel = this.$q.defer();
        this.polling.poll(100, pollObj.pollFn, () => false, cancel.promise);
		
		// Need a mechanism to wait for a second.
		// SleepForASecond().then(() => { cancel.resolve(); });
		
        expect(pollObj.pollFn).toHaveBeenCalledTimes(10);

    });

});

【问题讨论】:

  • 请提供您的真实代码,而不是一些抽象的情况。 我调用一个 poll() 函数,该函数使用 $interval 重复调用另一个函数 CheckTaskStatus() - 这很可能与 SleepForASecond 异步函数完全不同。
  • 更新了描述。

标签: angularjs jasmine


【解决方案1】:

AngularJS 异步代码和任意 JS 代码在 Jasmine 中的测试方式不同。

AngularJS 服务是专门为使测试同步而设计的,包括$interval

在测试中,您可以使用 $interval.flush(millis) 向前移动毫秒毫秒并触发计划在该时间运行的任何函数。

原来如此:

let cancel = this.$q.defer();
this.polling.poll(100, pollObj.pollFn, () => false, cancel.promise);

$interval.flush(1050);
expect(pollObj.pollFn).toHaveBeenCalledTimes(10);

在涉及非 Angular 异步单元的测试中有所不同。 Jasmine clock API 应改为使用。它修补了内置的定时器函数,以便与 Jasmine tick 方法同步执行。

beforeEach(() => {
  jasmine.clock().install();
});

afterEach(() => {
  jasmine.clock().uninstall();
});

it('...', () => {
  SleepForASecond().then(() => {
    expect(1).toBe(0);
  });
  jasmine.clock().tick(1050);
});

Jasmine 2.7 adds support 用于承诺和 async 功能。这允许无缝测试基于 Promise 的函数,但 Promise 会产生真正的延迟并导致异步测试:

it('...', async () => {
  await SleepForASecond();
  expect(1).toBe(0);
});

【讨论】:

  • 谢谢! @estus。那是非常有见地的。 $interval.flush() 为我工作。我使用 Jasmine 2.8.0,那么我可以使用异步函数吗?
  • 是的。但通常你在 Angular 测试中不需要它们(除非原生 Promise 涉及应用程序本身)。由于 $q 的工作方式,将 $q 承诺与原生承诺(包括 async)混合可能会导致糟糕的情况。
  • 当我点击返回按钮时,我看到'intervalPromise.catch(() => {});' 正在被执行。但是,在控制器的 onExit() 函数中,我正在解决取消承诺。理想情况下,这应该取消了 cancel.then() 块的间隔。但不知何故没有。有什么线索吗?
  • 另外,您能否就 Angular JS 承诺的错误处理最佳实践提出建议?让 catch 块忽略错误是明智的(我猜不是)?
  • 是的,错误应该总是被捕获。我建议使用 github.com/bvaughn/jasmine-promise-matchers 。它提供简洁的断言并处理错误。
【解决方案2】:

您可以使用 jasmine 的异步支持让测试用例验证等待。

请参阅Jasmine documentation 下的主题“异步支持”

在上面提到的情况下,测试用例应改为如下:

    //pass done as parameter to the 'it' call back function
    it('blah', function(done) {
       ...
       poll()
      // Mechanism for sleep
     SleepForASecond().then(function() {              
         expect();
         //jasmine will evaluate the test case only after done is called.
         done();
     });
    })

【讨论】:

  • 它会导致以下故障:PhantomJS 2.1.1 (Linux 0.0.0) basPolling 模块测试 blah FAILED 错误:超时 - 在 jasmine.DEFAULT_TIMEOUT_INTERVAL 指定的超时内未调用异步回调。 PhantomJS 2.1.1 (Linux 0.0.0): 执行 1 of 2271 (1 FAILED) (0 secs / 5.2PhantomJS 2.1.1 (Linux 0.0.0) SLOW 5.271 secs: myPolling module tests blah
猜你喜欢
  • 2014-12-12
  • 1970-01-01
  • 2015-07-26
  • 1970-01-01
  • 2023-03-23
  • 2014-02-21
  • 2011-06-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多