3

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.

kim_hart
  • 31
  • 1
  • 5
  • Im not sure what you are trying to do. When checking time elapsed, I will normally do something like `(current_epoch - created_epoch)` which will give seconds or ms, pending on if your epoch is for ms or s. – LostJon Mar 16 '17 at 18:39
  • This little diddy may help http://stackoverflow.com/questions/4631928/convert-utc-epoch-to-local-date-with-javascript – LostJon Mar 16 '17 at 18:42
  • My function correctly converts the time, that's not the issue. I'm wondering how you can unit test against something that's constantly changing (as the time elapsed will always be increasing) – kim_hart Mar 16 '17 at 19:07
  • see my answer response. You want to base line your `created_utc` with the current time – LostJon Mar 16 '17 at 19:17

2 Answers2

1

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.

caisah
  • 1,959
  • 1
  • 21
  • 31
0

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");
LostJon
  • 2,287
  • 11
  • 20
  • Probably the _best_ way to test this would be to refactor the function to accept a date as a param. Then there's no internal state assumed in the function at all. – Askdesigners Oct 21 '19 at 11:14