Search code examples
javascriptjestjsdate-fns

How can I test date-fns formatDistanceToNow with Jest


I'm trying to write a unit test with Jest for a function that is using the date-fns formatDistanceToNow function to display human readable distance for today. So if the date is today, it should show for example "in about 5 hours" but if the date is tomorrow just show "Tomorrow". Now it depends on the time, the test is running if "in 5 hours" is still today or already tomorrow.

How can I set a fixed date and time the test is running in? I guess formatDistanceToNow is using something like Date.now() as a reference which is using the system time.

Here is an example.

date-util.js

import { isToday, isTomorrow, formatDistanceToNow } from 'date-fns';

export function formatDate(date) {
  if (isToday(date)) {
    return formatDistanceToNow(value, { addSuffix: true });
  }

  if (isTomorrow(value)) {
    return 'Tomorrow';
  }
}

date-util.spec.js

describe('date-util', () => {
  it('should show today date', () => {
    const today = new Date();
    
    expect(formatDate(today)).toBe('Today');
    // plus 5 minutes
    let testDate = new Date(today.getTime() + 5 * 60000);
    expect(formatDate(testDate)).toBe('in 5 minutes');
    // minus 5 minutes
    testDate = new Date(today.getTime() - 5 * 60000);
    expect(formatDate(testDate)).toBe('5 minutes ago');
    // plus 4 hours and 20 minute
    testDate = new Date(today.getTime() + 260 * 60000);
    expect(formatDate(testDate)).toBe('in about 4 hours');
    // minus 2 hours and 1 minute
    testDate = new Date(today.getTime() - 121 * 60000);
    expect(formatDate(testDate)).toBe('about 2 hours ago');
  });

  it('should show tomorrow date', () => {
    const tomorrow = new Date();
    tomorrow.setDate(tomorrow.getDate() + 1);

    expect(formatDate(tomorrow)).toBe('Tomorrow');
    // 5 minutes ahead
    let testDate = new Date(tomorrow.getTime() + 5 * 60000);
    expect(formatDate(testDate)).toBe('Tomorrow');
    // minus 5 minutes
    testDate = new Date(tomorrow.getTime() - 5 * 60000);
    expect(formatDate(testDate)).toBe('Tomorrow');
  });
});

In the test I'm adding 4 hours and 20 minutes. If the test runs at 8pm, it won't show "in about 4 hours" because it's already tomorrow.


Solution

  • Jest provides a solution to set a fixed date for the date object in all tests. I added this to the test to make it work independent of the daytime. The date being used as "now" in all tests is today at 12:00.

    import { set } from 'date-fns';
    
    describe('date-util', () => {
      beforeAll(() => {
        const today = set(new Date(), { hours: 12, minutes: 0, seconds: 0, milliseconds: 0 });
        jest.useFakeTimers({ now: today });
      });
    
      afterAll(() => {
        jest.useRealTimers();
      });
    
      it('should show today date', () => {
        // tests
      });
    
      it('should show tomorrow date', () => {
        // tests
      });
    });