Skip to main content

Clock Mocking

TestClock lets you freeze or advance the browser's Date object so you can test time-sensitive UI without touching the database or the system clock.


How it works​

clock().set(isoString) injects a Date override into the active browser page. Every subsequent new Date() and Date.now() call in client-side JavaScript returns the mocked time. The real Date is saved under window.__sbOriginalDate and restored automatically at the end of each test.


Quick example​

public class TrialBannerTest extends BaseTest {

@Test
public void showsExpiredBanner_when30DaysPast() {
open("/dashboard");
clock().set("2030-06-01T00:00:00Z"); // trial expired 30 days ago
getDriver().navigate().refresh(); // page re-renders with mocked time

assertThat(By.id("trial-banner")).hasText("Your trial expired 30 days ago");
}
}
When to call open() first

clock().set() requires an active page because it injects JavaScript. Call open() first, then set the clock, then trigger any client-side re-render (refresh, SPA navigation, or a click that fetches dates).


API reference​

clock().set(String isoDateTime)​

Overrides new Date() and Date.now() in the browser to the given instant.

clock().set("2030-01-01T00:00:00Z");
  • Accepts any ISO 8601 UTC string (Instant.parse compatible)
  • Returns this for chaining

clock().advance(Duration duration)​

Advances the mocked time by duration from the current mock. If no mock is active, advances from the real current time.

clock().set("2030-01-01T00:00:00Z");
clock().advance(Duration.ofDays(30)); // now mocked to 2030-01-31

Returns this for chaining. Common durations:

Duration.ofSeconds(30)
Duration.ofMinutes(5)
Duration.ofHours(1)
Duration.ofDays(90)

clock().reset()​

Restores the real Date implementation in the browser. Called automatically after each test — explicit calls are optional.

clock().set("2030-01-01T00:00:00Z");
// ... assertions ...
clock().reset(); // optional — framework does this automatically

clock().getMockedTimeMs()​

Returns the currently mocked time as epoch milliseconds, or null if no mock is active.


Common use cases​

Trial / subscription expiry​

// Move 30 days past the trial end date
clock().set("2030-06-01T00:00:00Z");
getDriver().navigate().refresh();
assertThat(By.id("trial-expired-banner")).isVisible();

Upcoming promotion not yet active​

// One day before promotion starts — button should be hidden
clock().set("2030-04-30T23:59:59Z");
getDriver().navigate().refresh();
assertThat(By.id("promo-btn")).isHidden();

// Day of promotion — button should appear
clock().advance(Duration.ofSeconds(2));
getDriver().navigate().refresh();
assertThat(By.id("promo-btn")).isVisible();

Countdown timers​

open("/sale");
clock().set("2030-07-04T11:00:00Z");
getDriver().navigate().refresh();
assertThat(By.id("countdown")).containsText("1 hour remaining");

Scheduled job indicator​

clock().set("2030-12-31T23:59:00Z");
getDriver().navigate().refresh();
assertThat(By.id("year-end-notice")).isVisible();

Chaining​

open("/account");
clock()
.set("2030-01-01T00:00:00Z")
.advance(Duration.ofDays(365)); // now at 2031-01-01
getDriver().navigate().refresh();
assertThat(By.id("anniversary-badge")).isVisible();

Auto-reset​

The framework calls TestClock.autoReset() after every test in all outcome paths (pass, fail, skip). Each test starts with a clean, real Date. You never need to call clock().reset() explicitly.


Scope​

TestClock replaces Date in the active page. On local Chromium drivers (ChromiumDriver: Chrome, Edge), it also registers a CDP script to reapply the override after refresh and navigation in that browser context. The refresh examples above require this CDP support. With RemoteWebDriver (Selenium Grid, BrowserStack, Sauce Labs) no CDP script is registered, so the mock is lost on refresh or navigation — call clock().set(...) again after navigating. If CDP registration fails, TestFly logs a warning and the mock applies only to the current document. On other browsers, call clock().set(...) again after navigation; avoid relying on a mocked date during the next page's initial load.

It does not affect:

  • Server-side date/time checks (use a server-side date override or environment variable for those)
  • Other browser tabs / windows
  • Java code running in the test JVM

Config​

testfly.yml
clock:
injectHeader: false # send X-Mock-Date header to server with each request
headerName: X-Mock-Date # header name (requires browser CDP support)

injectHeader is off by default. When enabled, every browser request will include the X-Mock-Date header set to the current mock time, allowing backend services that respect this header to simulate the same date server-side.