kim_hart
kim_hart

Reputation: 31

How to unit test against a dynamic date/time with Mocha, Chai, Sinon

I'm new to unit testing, and I need to test a function that measures the time elapsed since a reddit post was created. Hard coding the arguments works right now, but the tests will fail as time moves along.

For instance, a test that outputs "1 day ago" with a hard-coded argument will pass today, but won't pass tomorrow, as it then will have been 2 days since the post. Same concept applies when measuring hours.

Here's my function that converts the created_utc timestamp (a number like 1488420682) to a human-readable amount of time elapsed:

PostCluster.js

getElapsedHours(created_utc) {
  const seconds = created_utc;
  const epoch = new Date(1970, 0, 1);
  const timeStamp = new Date(epoch.setSeconds(seconds));
  const now = new Date();
  const delta = timeStamp - now;
  const currentTimeZoneOffsetInHours = now.getTimezoneOffset() / 60;
  const hoursElapsed = Math.floor(Math.abs((delta / 1000) / 3600) + currentTimeZoneOffsetInHours);

  if (hoursElapsed > 24) {
    const days = Math.floor(hoursElapsed / 24);
    if (hoursElapsed > 48) {
      return `${days} days`;
    } return '1 day';
  } else {
    if (hoursElapsed === 1) {
      return '1 hour';
    } else if (hoursElapsed < 1) {
      return 'less than 1 hour';
    } else {
      return `${hoursElapsed} hours`;
    }
  }
}

Here's an example of the outputs displayed in action

The test below passes as is, because the time elapsed since that timestamp will always be greater than 48 hours. BUT... how would I write the tests to check for the outputs "1 day, 1 hour, x hours, less than 1 hour" and have them pass every time they run in the future?

postcluster.spec.js

describe('<PostCluster />', () => {

  it("ensures getElapsedHours returns 'x days' when greater than 48 hours", () => {
    const days = PostCluster.prototype.getElapsedHours(1488420682).split(' ')[1];
    expect(days).to.equal('days');
  });

});

I've seen MockDate used, but not sure how that can apply here.

Upvotes: 3

Views: 6461

Answers (2)

LostJon
LostJon

Reputation: 2387

Ok...I may have commented prematurely, but the below may work for you

function demo(created_utc) { 
    var elapsed_seconds = (new Date() - (created_utc * 1000) ) / 1000
    if (elapsed_seconds >= 172800) return Math.floor(elapsed_seconds /86400).toString() + " days ago";
    else if(elapsed_seconds >= 86400) return "about 1 day ago"
    else if(elapsed_seconds >= 7200) return Math.floor(elapsed_seconds /3600).toString() + " hours ago";
    else if(elapsed_seconds >= 3600) return "about an hour ago"
    else return "less than an hour ago"
};

To test this function I would probably data drive this always with the current time. So, I would need to figure out created_utc to properly test each scenario correctly.

To do this, In my expect statement, i would have something like

var epoch_hour = (new Date() / 1000) - 3600 
expect(demo(epoch_hour)).to.contain("about an hour");

Upvotes: 0

caisah
caisah

Reputation: 2087

You should create some mock data for every case:

  • 1 day
  • x days
  • 1 hour
  • less than 1 hour
  • x hours

For example for x days you generate a new date that is older with x days than today:

it("ensures getElapsedHours returns 'x days' when greater than 48 hours", () => {
    var days = 3;
    var moreThanThreeDaysInMillisecs = 1000 * 3601 * days * 24;
    var dateMoreThanThreeDays = Date.now() - moreThanThreeDaysInMillisecs;
   ...
})

and test against that.

You should not test with live data as it is volatile and your tests could fail because of that. Create some mock data and test each public function/method in isolation.

Upvotes: 1

Related Questions