用摩卡柴-西农测试去弹跳

时间:2019-02-20 10:56:02

标签: mocha sinon chai debounce

我想测试我的debounce函数,但是下划线或lodash也是一样。

我想使用摩卡咖啡,柴的assert,甚至可能使用sinon。

我准备了codepen

西农的计时器让我头疼。当我要求我的去抖操作首先运行时,我仍然收到一个响应,该信息表明未调用去抖回调,而实际上是这样!

让我们看看代码:

// sinon timers
it('should not run debounced function too early', function() { 
  var clock = sinon.useFakeTimers();
  var delay = 100;
  var targetFn = sinon.spy();
  var delayedFn = g3.debounce(targetFn, {delay: delay, fireStart: false, fireLast: true});
  // for lodash:
  //var delayedFn = _.debounce(targetFn, delay, {leading:false, trailing:true});
  delayedFn();
  clock.tick(delay - 1);
  clock.restore(); 
  sinon.assert.notCalled(targetFn); 
});

当我们更改控制第一个/最后一个运行的值时,我们从下划线得到错误的结果。有人可以提出替代方案吗?更正吗?

我还发现了this链接,可能是在茉莉花中进行的测试。

我想将下一个块修改为摩卡咖啡:

it('should only happen once', function(){
    var count = 0;
    runs(function(){
        var fn = underscore.debounce(function(){
            count += 1;
        }, 100);

        fn();
        fn();
        fn();
    });

    waitsFor(function(){
        return count > 0;
    }, 'didnt execute', 200);

    runs(function(){
        expect(count).toBe(1);
    });
});

这可能吗?

我只能看到此摩卡测试成功:

  it('shouldnt execute immediately', function(){
    var hasHappened = false;

    var fn = g3.debounce(function(){
        hasHappened = true;
    }, {fireFirst: false, fireLast: true, delay: 100});

    chai.assert(hasHappened === false, 'error: callback has called');
    fn();
    chai.assert(hasHappened === false, 'error: callback has called');
  });

如果您知道一堆测试用例,那就很好了,这样我就可以看到我的错误。

1 个答案:

答案 0 :(得分:0)

我终于found为sinon越野车计时器提供了解决方案,因为它们在内部覆盖了Date对象,setTimeout()setInterval()等。

这里的问题是,我们是否愿意更改代码以摆脱测试的束缚,甚至更糟地将测试条件集成到我们的代码中。

解决方案是使用...时间启动计时器!

/*** file 'g3.debounceTest.js' ***/
describe('g3.debounce', function() {
  it('should not run debounced function with: 1 call at elapsed < delay & fireLast: true', function() { 
    var clock = sinon.useFakeTimers();
    // sinon remedy avoids buggy behavior
    clock.tick(1000);
    //console.log(Date.now());
    var delay = 100;
    var spy = sinon.spy();
    var targetFn = function(){console.log('eventually debounced was called!'); spy()};
    var delayedFn = g3.debounce(targetFn, {delay: delay, fireFirst: false, fireLast: true});
    // for lodash:
    //var delayedFn = _.debounce(targetFn, delay, {leading: false, trailing: true});
    delayedFn();
    clock.tick(delay - 1);
    //console.log(Date.now());
    clock.restore(); 
    sinon.assert.notCalled(spy); 
  });

  it('should run debounced function once with: 1 call at elapsed < delay & fireFirst: true', function() { 
    var clock = sinon.useFakeTimers();
    // sinon remedy avoids buggy behavior
    clock.tick(1000);
    //console.log(Date.now());
    var delay = 100;
    var spy = sinon.spy();
    var targetFn = function(){console.log('eventually debounced was called!'); spy()};
    var delayedFn = g3.debounce(targetFn, {delay: delay, fireFirst: true, fireLast: false});
    // for lodash:
    //var delayedFn = _.debounce(targetFn, delay, {leading: true, trailing: false});
    delayedFn();
    clock.tick(delay - 1);
    //console.log(Date.now());
    clock.restore(); 
    sinon.assert.calledOnce(spy); 
  });

  it('should run debounced function once with: 3 calls at elapsed < delay & fireFirst: true', function() { 
    var clock = sinon.useFakeTimers();
    // sinon remedy avoids buggy behavior
    clock.tick(1000);
    //console.log(Date.now());
    var delay = 100;
    var spy = sinon.spy();
    var targetFn = function(){console.log('eventually debounced was called!'); spy()};
    var delayedFn = g3.debounce(targetFn, {delay: delay, fireFirst: true, fireLast: false});
    // for lodash:
    //var delayedFn = _.debounce(targetFn, delay, {leading: true, trailing: false});
    /* 1 */delayedFn();
    /* after 99ms */
    clock.tick(delay - 1);
    //console.log(Date.now());
    /* 2 */delayedFn();
     /* after 99ms */
    clock.tick(delay - 1);
    //console.log(Date.now());
    /* 3 */delayedFn();
    clock.restore(); 
    sinon.assert.calledOnce(spy); 
  });

  it('should run debounced function once with: 3 calls at elapsed < delay & fireLast: true', function() { 
    var clock = sinon.useFakeTimers();
    // sinon remedy avoids buggy behavior
    clock.tick(1000);
    //console.log(Date.now());
    var delay = 100;
    var spy = sinon.spy();
    var targetFn = function(){console.log('eventually debounced was called!'); spy()};
    var delayedFn = g3.debounce(targetFn, {delay: delay, fireFirst: false, fireLast: true});
    // for lodash:
    //var delayedFn = _.debounce(targetFn, delay, {leading: true, trailing: false});
    /* 1 */delayedFn();
    /* after 99ms */
    clock.tick(delay - 1);
    //console.log(Date.now());
    /* 2 */delayedFn();
     /* after 99ms */
    clock.tick(delay - 1);
    //console.log(Date.now());
    /* 3 */delayedFn();
    /* after 100ms */
    clock.tick(delay);
    clock.restore();
    sinon.assert.calledOnce(spy);
  });
});
/***end file 'g3.debounceTest.js' ***/

我还想在回调中注入spy(),因为我想看看回调发生了什么,我惊讶地注意到,回调在我们调用clock.restore();后运行,但sinon却没有有一种方法可以对此进行审核,并且完全不了解clock.restore();之后发生的情况。

这就是为什么我在最后一种情况下不得不写clock.tick(delay);以便允许sinon“看到”上一次调用的原因。

最后,如果您有更多想法来测试这样的异步代码,我很乐意看到它们。