पाठ 9 / 25
Dates and Randomness
Pin the clock and control random values.
Make non-deterministic inputs deterministic
Code that calls new Date(), Date.now() or Math.random() gives different results on each run, and date logic often breaks only at midnight, month ends or in another time zone. Options, from simplest: pass the current time or a random source in as a parameter (easiest to test, cleaner design); use vi.setSystemTime(date) / jest.setSystemTime(date) with fake timers to pin Date; and spy on Math.random with mockReturnValue for a fixed sequence. Also pin the time zone for the test process, for example by setting the TZ environment variable in the test script, so date formatting tests behave the same on every laptop and CI machine.
Pinned date and fixed random values
Vitest; Jest has jest.setSystemTime and jest.spyOn.
import { afterEach, expect, it, vi } from 'vitest';
import { isTrialExpired, pickWinner } from './promo';
afterEach(() => {
vi.useRealTimers();
vi.restoreAllMocks();
});
it('expires the trial after 14 days', () => {
vi.useFakeTimers();
vi.setSystemTime(new Date('2025-03-15T12:00:00Z'));
expect(isTrialExpired(new Date('2025-03-01T12:00:00Z'))).toBe(true);
expect(isTrialExpired(new Date('2025-03-02T12:00:00Z'))).toBe(false);
});
it('picks the entry at the random index', () => {
vi.spyOn(Math, 'random').mockReturnValue(0.5);
expect(pickWinner(['a', 'b', 'c', 'd'])).toBe('c'); // floor(0.5 * 4) = 2
});
// package.json: "test": "TZ=UTC vitest" (bash syntax; on Windows use a tool such as cross-env)Inject the clock in new code
A function like isExpired(start, now = new Date()) needs no fakes at all in tests: pass the now you want. Reserve global time mocking for code you cannot easily change.
त्वरित जाँच: Which approach makes a date-dependent function easiest to test?
- Use real timers and retry on failure
- Run the tests only at midnight
- Pass the current time in as a parameter with a sensible default
- Snapshot the output of new Date()
Answer
Pass the current time in as a parameter with a sensible default — Injected time needs no global mocks.