Mocking Time with sinon.js

While writing tests for an Angular app, one scenario required a page to behave differently depending on whether the time of day was before or after 6 AM. The logic was simple enough to include in the controller:

vm.date = moment().subtract(6, 'hours').toDate(); 

The intended behavior: if the time is before 6 AM, display the previous day; otherwise, display today. However, the challenge in testing this controller was controlling the current time to evaluate both outcomes.

Sinon.js (http://sinonjs.org/) is a commonly used library for mocking dependencies in Angular tests. One of its features, useFakeTimers, is especially useful for handling time-dependent scenarios. With useFakeTimers, the time within a test can be set to any value. The following demonstrates how to simulate a page load before 6 AM:

clock = sinon.useFakeTimers(new Date(2016,2,15).getTime()); //sets date to Mar 15, 2016, at 00:00 
clock.tick(60*60*2*1000); //move the fake time ahead to 2AM 

By passing in a specific date to useFakeTimers, tests can define any starting point. The clock.tick() function allows precise manipulation of the time.  

Pro Tip: Remember to call clock.restore() to reset the date/time in the afterEach function of your specs.

Putting it all together

The useFakeTimers feature in sinon.js made this a straightforward scenario to test.

'use strict';

describe('time specs', function() {
  var scope, controller;
  var clock, moment;

  beforeEach(module('app'));

  beforeEach(inject(function($rootScope, $controller, _moment_) {
    scope = $rootScope.$new();
    moment = _moment_;
  }));

  describe('when the page loads', function() {
    describe('and it is earlier than 6AM', function() {
      beforeEach(inject(function($controller) {
        clock = sinon.useFakeTimers(new Date(2016, 2, 15).getTime()); // sets date to Mar 15, 2016 at 00:00
        clock.tick(60 * 60 * 2 * 1000); // move the fake time ahead to 2AM
        controller = $controller('PageController', {
          $scope: scope
        });
        scope.vm = controller;
      }));

      it('then the date should default to yesterday', function() {
        expect(moment(scope.vm.date).format('MM-DD-YYYY')).to.be.equal(moment().subtract(1, 'day').format('MM-DD-YYYY'));
      });
    });
    describe('and it is later than 6AM', function() {
      beforeEach(inject(function($controller) {
        clock = sinon.useFakeTimers(new Date(2016, 2, 5).getTime()); // sets date to Mar 5, 2016 at 00:00
        clock.tick(60 * 60 * 9 * 1000); // move the fake time ahead to 9AM
        controller = $controller('PageController', {
          $scope: scope
        });
        scope.vm = controller;
      }));
      it('then the date should default to today', function() {
        expect(moment(scope.vm.date).format('MM-DD-YYYY')).to.be.equal(moment().format('MM-DD-YYYY'));
      });
    });
  });
  afterEach(function() {
    clock.restore();
  });
});

Looking for a new job? We work with some of the biggest names in tech, and we’re hiring! Check out our open jobs and make your next career move with Planet.

The Planet Group Logo symbol
Let’s Partner Together
Contact us today for expert talent solutions or career-defining opportunities.