【问题标题】:Mocking dates in AngularJS / Jasmine testsAngularJS / Jasmine 测试中的模拟日期
【发布时间】:2013-06-09 19:04:00
【问题描述】:

我有一个指令在几个函数中多次初始化 Date 对象。 当对单个函数进行单元测试时,我可以像这样处理存根日期:

(function (global) {
  var NativeDate = global.Date;

  global.stubDateConstructor = function (fakeDate) {
      global.Date = function () {
          global.Date = NativeDate;
          return fakeDate;
      }
  }
}(this));

// ageInYears()
it("should return the age in years of the person given his/her birthdate", function() {
    stubDateConstructor(new Date('2010/01/01'));
    expect(ageInYears('01-01-1990')).toBe(20);
    stubDateConstructor(new Date('2010/01/01'));
    expect(ageInYears('01-01-1900')).toBe(110);
});

为了对指令本身进行单元测试,它调用ageInYears 和其他几个类似的函数,因为我在一次调用Date() stubDateConstructor 后会将Date() 重置为真正的Date 对象。

AngularJS / Jasmine 中是否有本地方式来处理这些情况,或者我应该研究一下 Sinon,例如?

【问题讨论】:

  • 现在我开始意识到我可能必须初始化一次 date 并将其传递给需要它的函数..也许这是最好的解决方案?
  • 我自己是 Timecop.js 的粉丝。任何具有 Timecop.travel 和 Timecop.freeze 等功能的库都会得到我的认可。
  • 看起来 sinon.js 还可能提供适用于日期的时间模拟(取决于浏览器支持)sinonjs.org/docs/#clock

标签: unit-testing date angularjs


【解决方案1】:

angular.mock.TzDate 在这里会是更好的原生替代品。这是 angular mocks 的助手,真正保护您的测试免受系统时区或任何其他依赖项的影响

https://docs.angularjs.org/api/ngMock/type/angular.mock.TzDate

这很适合茉莉花或摩卡咖啡

【讨论】:

    【解决方案2】:

    Jasmine (2.2) 时钟可以模拟日期和时间。

    http://jasmine.github.io/2.2/introduction.html#section-Mocking_the_Date

    例如(来自文档):

    it("mocks the Date object and sets it to a given time", function() {
      var baseTime = new Date(2013, 9, 23);
      jasmine.clock().mockDate(baseTime);
    
      jasmine.clock().tick(50);
      expect(new Date().getTime()).toEqual(baseTime.getTime() + 50);
    });
    

    【讨论】:

    • 谢谢,当我问的时候它似乎不存在,但现在看来是正确的工具
    【解决方案3】:

    我能够使用sinon's fake timers 的组合来模拟窗口的计时器和角度的mock interval service 的组合来模拟时间变化。在这里,被测试的 countDownService 在内部使用了 javscript Date 和 angular 的普通 interval service 类似的东西:

      describe('when start time was 3000 milliseconds and 1001 milliseconds have passed', function() {
        var startTime;
        var elapse;
        beforeEach(function(){
          this.clock = sinon.useFakeTimers();
          startTime = 3000;
          elapse = 1001;
        });
    
        var elapseMillis = function(intervalMock,sinonClock,millis){
          sinonClock.tick(millis);
          intervalMock.flush(millis);
        };
    
        it('elapsedMillis + timeRemainingMillis should == startime', 
          inject(function($rootScope,$interval,countdownService) {
            countdownService.startTimer(startTime);
            elapseMillis($interval,this.clock,elapse);
            //jasmine clock does not mock Date
            //see https://github.com/pivotal/jasmine/issues/361
            var elapsedMillis = countdownService.getElapsedMillis();
            var timeRemainingMillis = countdownService.getTimeRemainingMillis();
            expect(elapsedMillis + timeRemainingMillis).toEqual(startTime);
            expect(elapsedMillis).toEqual(elapse);
        }));
    
        afterEach(function(){
          this.clock.restore();
            startTime = 0;
            elapse = 0;
        });
      });
    

    您还需要确保并将 sinon js 的 sinon-timers-1.8.1.js 包含在您的 karma.conf.js 文件属性中。

    【讨论】:

      【解决方案4】:

      一个简单的解决方案是创建一个 Angular Dates 服务,为您提供 Date 对象 - 它甚至可能只有一个方法 - Dates.now() - 通过返回 new Date() 发送回当前日期.然后,您可以在需要获取当前日期时使用此服务。

      这允许您在单元测试时注入不同的Dates 服务,例如在调用时始终返回您选择的特定日期而不是当前时间的服务。

      【讨论】:

      • 如此简单,我的脑海里正在徘徊着各种愚蠢的过于复杂的场景。谢谢兄弟!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-07
      • 2013-10-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多