Jest Mock `new Date` in TypeScript

When writing unit tests in TypeScript projects, you often encounter scenarios where you need to control the current date and time. For instance, if your code depends on the current date for tasks like calculating deadlines, logging timestamps, or handling time-sensitive operations, it can be challenging to write reliable and repeatable tests. Jest, a popular JavaScript testing framework, provides powerful mocking capabilities that allow you to mock the new Date constructor in TypeScript. This blog post will guide you through the fundamental concepts, usage methods, common practices, and best practices of mocking new Date in a TypeScript project with Jest.

Table of Contents#

  1. Fundamental Concepts
  2. Usage Methods
  3. Common Practices
  4. Best Practices
  5. Conclusion
  6. References

Fundamental Concepts#

What is Mocking?#

Mocking is a technique used in unit testing to replace parts of the system under test with mock objects. These mock objects simulate the behavior of real objects, allowing you to isolate the unit of code you are testing and control the inputs and outputs.

Why Mock new Date?#

The Date object in JavaScript returns the current date and time when instantiated with new Date(). In unit tests, the current date and time can vary, which makes tests non-deterministic. By mocking new Date, you can set a fixed date and time, ensuring that your tests are repeatable and reliable.

How Jest Mocks Work#

Jest provides a jest.fn() function to create mock functions. You can use this function to replace the Date constructor with a mock function that returns a fixed date.

Usage Methods#

Basic Mocking#

The simplest way to mock new Date in Jest is to replace the global Date constructor with a mock function.

// Example function that uses new Date
function getCurrentDate() {
    return new Date();
}
 
describe('getCurrentDate', () => {
    it('should return a mocked date', () => {
        const mockDate = new Date('2023-01-01T00:00:00');
        jest.spyOn(global, 'Date').mockImplementation(() => mockDate as unknown as DateConstructor);
 
        const result = getCurrentDate();
        expect(result).toEqual(mockDate);
 
        // Restore the original Date constructor
        (global.Date as jest.Mock).mockRestore();
    });
});

In this example, we first create a mock date. Then we use jest.spyOn to replace the global Date constructor with a mock function that always returns our mock date. After the test, we restore the original Date constructor to avoid affecting other tests.

Mocking with Different Dates in Multiple Tests#

You can also use different mock dates in different tests.

describe('Multiple tests with different mock dates', () => {
    it('should return the first mocked date', () => {
        const mockDate1 = new Date('2023-01-01T00:00:00');
        jest.spyOn(global, 'Date').mockImplementation(() => mockDate1 as unknown as DateConstructor);
 
        const result1 = getCurrentDate();
        expect(result1).toEqual(mockDate1);
 
        (global.Date as jest.Mock).mockRestore();
    });
 
    it('should return the second mocked date', () => {
        const mockDate2 = new Date('2023-02-02T00:00:00');
        jest.spyOn(global, 'Date').mockImplementation(() => mockDate2 as unknown as DateConstructor);
 
        const result2 = getCurrentDate();
        expect(result2).toEqual(mockDate2);
 
        (global.Date as jest.Mock).mockRestore();
    });
});

Common Practices#

Using a Helper Function#

To avoid code duplication, you can create a helper function to mock the date.

function mockDate(date: string) {
    const mockDate = new Date(date);
    jest.spyOn(global, 'Date').mockImplementation(() => mockDate as unknown as DateConstructor);
    return () => {
        (global.Date as jest.Mock).mockRestore();
    };
}
 
describe('Using helper function to mock date', () => {
    it('should return a mocked date', () => {
        const restoreDate = mockDate('2023-03-03T00:00:00');
 
        const result = getCurrentDate();
        const expected = new Date('2023-03-03T00:00:00');
        expect(result).toEqual(expected);
 
        restoreDate();
    });
});

Testing Time-Sensitive Logic#

If your code has time-sensitive logic, such as checking if a date is in the future or past, you can use mocked dates to test different scenarios.

function isDateInFuture(date: Date) {
    const currentDate = new Date();
    return date > currentDate;
}
 
describe('isDateInFuture', () => {
    it('should return true for a future date', () => {
        const restoreDate = mockDate('2023-01-01T00:00:00');
        const futureDate = new Date('2023-02-02T00:00:00');
 
        const result = isDateInFuture(futureDate);
        expect(result).toBe(true);
 
        restoreDate();
    });
 
    it('should return false for a past date', () => {
        const restoreDate = mockDate('2023-02-02T00:00:00');
        const pastDate = new Date('2023-01-01T00:00:00');
 
        const result = isDateInFuture(pastDate);
        expect(result).toBe(false);
 
        restoreDate();
    });
});

Best Practices#

Isolate Mocking#

Make sure to isolate the mocking of new Date to the specific test or test suite where it is needed. Always restore the original Date constructor after the test to avoid side effects on other tests.

Use Descriptive Mock Dates#

When creating mock dates, use descriptive dates that clearly represent the scenario you are testing. For example, if you are testing a function that processes monthly data, use the first day of the month as a mock date.

Test with Different Time Zones#

If your application deals with different time zones, consider testing with mock dates from different time zones to ensure the code works correctly under various conditions.

Conclusion#

Mocking new Date in TypeScript projects using Jest is a powerful technique that allows you to write reliable and repeatable unit tests for code that depends on the current date and time. By understanding the fundamental concepts, using the right usage methods, following common practices, and adhering to best practices, you can effectively control the date and time in your tests and ensure the quality of your code.

References#