tempo/mock

Provides functions to mock the system time as seen by the tempo package for testing purposes.

There are four main ways to mock time for testing in this package:

Freezing the system time

By freezing the system time to a specific time so that calls to the current system time will return a known value, you can reliably test code that gets the current system time once. Frozen time can also be warped, allowing for fine-grained control over the system time. More on that below.

import tempo
import tempo/mock
import gleeunit/should

pub fn format_system_time() {
  tempo.format_utc(tempo.Custom("dddd @ HH:mm:ss"))
}

pub fn format_system_time_test() {
  // We can test that this function returns the expected value by first
  // freezing the system time to a specific, known time.
  mock.freeze_time(datetime.literal("2024-06-21T11:10:35.000Z"))

  format_system_time()
  |> should.equal("Friday @ 11:10:35")

  mock.unfreeze_time()
}

Setting the current system time to a reference time

By setting the current system time to a specific reference time and
letting it progress forward from there, you can reliably test code that may get the system time multiple times but executes different logic depending on the date or time of day.

An example of this would be complex, so the implementation is left out.

import app
import tempo
import tempo/mock
import gleeunit/should

pub fn thursday_run_test() {
  // We can test that this function returns the expected value on Thursdays
  // by first setting the current system time to a reference time.
  mock.set_time(datetime.literal("2024-06-20T11:10:35.000Z"))

  app.run()
  |> should.equal(42)

  mock.unset_time()
}

Warping system time instead of sleeping

By changing sleep operations to time warps, you can instantly test any function with sleeps in it. Sleeps will warp frozen time when this is enabled.

import app
import tempo
import tempo/instant
import tempo/mock
import gleeunit/should

pub fn do_logic_after_sleep() {
 tempo.sleep(duration.seconds(10))
 app.do_logic()
}

pub fn do_logic_after_sleep_test() {
 tempo.enable_sleep_warp()

 let start = instant.now()
 do_logic_after_sleep()

 // This test executes immediately, but it appears to have taken 10
 // seconds to this package.
 instant.since(start) |> duration.as_seconds |> should.equal(10)

 tempo.disable_sleep_warp()
}

Manually warping system time

By manually warping the system time by a specific duration, you can instantly run logic after exact durations. You can also warp time when it is frozen to manually progress it.

import app
import tempo
import tempo/mock

pub fn test_spaced_calls() {
  mock.freeze_time(datetime.literal("2024-06-21T00:10:00.000Z"))

  app.do_side_effect()
  |> should.equal(Ok(Nil))

  mock.warp_time(duration.minutes(30))

  app.do_side_effect()
  |> should.equal(Ok(Nil))

  mock.unfreeze_time()
  mock.reset_warp_time()
}

Functions

pub fn disable_sleep_warp() -> Nil

Disables warping of the system time (as seen by this package) instead of sleeping when calls to tempo.sleep are made.

Examples

mock.enable_sleep_warp()
do_some_sleepy_test()
mock.disable_sleep_warp()
pub fn enable_sleep_warp() -> Nil

Enables warping of the system time (as seen by this package) instead of sleeping when calls to tempo.sleep are made. This is useful for instantly testing code that may have sleeps in it.

Examples

mock.enable_sleep_warp()
tempo.sleep(duration.seconds(10))
// -> This will warp perceived system time to 10 seconds in the future 
// instead of waiting for 10 real seconds.
pub fn freeze_time(at datetime: DateTime) -> Nil

Freezes the current system time (as seen by this package) to the provided datetime. Time will not progress until the ‘unfreeze_time’ function is called.

Examples

mock.freeze_time(datetime.literal("2024-06-21T00:10:00.000Z"))
tempo.format_utc(tempo.ISO8601Seconds)
// -> "2024-06-21T00:10:00Z"
process.sleep(duration.seconds(10))
tempo.format_utc(tempo.ISO8601Seconds)
// -> "2024-06-21T00:10:00Z"
pub fn reset_warp_time() -> Nil

Resets the warp time (as seen by this package) back to the real system time. This will clear any warp time added by either mock.warp_time or mock.enable_sleep_warp with subsequent calls to tempo.sleep.

Examples

mock.enable_sleep_warp()
tempo.sleep(duration.seconds(10))
tempo.format_utc(tempo.ISO8601Seconds)
// -> "2024-06-21T00:10:00Z"
mock.reset_warp_time()
tempo.format_utc(tempo.ISO8601Seconds)
// -> "2024-06-21T00:00:00Z"
pub fn set_time(to datetime: DateTime) -> Nil

Sets the current system time (as seen by this package) to the provided datetime, allowing time to progress after with a speedup factor. If the speedup is 1.0, then time will progress at the normal rate after being set. If the speedup is less than 1.0, time will progress slower than normal. If the speedup is greater than 1.0, time will progress faster than normal. This can be useful for quickly testing logic that may periodically wait for some amount of time.

Examples

mock.set_time(datetime.literal("2024-06-21T00:10:00.000Z"), 1.0)
tempo.format_utc(tempo.ISO8601Seconds)
// -> "2024-06-21T00:00:00Z"
process.sleep(duration.seconds(10))
tempo.format_utc(tempo.ISO8601Seconds)
// -> "2024-06-21T00:00:10Z"
mock.set_time(datetime.literal("2024-06-21T00:10:00.000Z"), 0.5)
tempo.format_utc(tempo.ISO8601Seconds)
// -> "2024-06-21T00:00:00Z"
process.sleep(duration.seconds(10))
tempo.format_utc(tempo.ISO8601Seconds)
// -> "2024-06-21T00:00:05Z"
mock.set_time(datetime.literal("2024-06-21T00:10:00.000Z"), 3.0)
tempo.format_utc(tempo.ISO8601Seconds)
// -> "2024-06-21T00:00:00Z"
process.sleep(duration.seconds(10))
tempo.format_utc(tempo.ISO8601Seconds)
// -> "2024-06-21T00:00:30Z"
pub fn unfreeze_time() -> Nil

Unfreezes the current system time (as seen by this package) back to the real system time.

Examples

mock.freeze_time(datetime.literal("2024-06-21T00:10:00.000Z"))
tempo.format_utc(tempo.ISO8601Seconds)
// -> "2024-06-21T00:10:00Z"
mock.unfreeze_time()
tempo.format_utc(tempo.ISO8601Seconds)
// -> "2025-01-31T22:48:00.000Z"
pub fn unset_time() -> Nil

Resets the current system time (as seen by this package) back to the real system time.

Examples

mock.set_time(datetime.literal("2024-06-21T00:10:00.000Z"), 1.0)
tempo.format_utc(tempo.ISO8601Seconds)
// -> "2024-06-21T00:00:00Z"
mock.unset_time()
tempo.format_utc(tempo.ISO8601Seconds)
// -> "2025-02-01T09:12:00Z"
pub fn warp_time(by duration: Duration) -> Nil

Warps the current system time (as seen by this package) by the provided duration. This is useful for instantly testing code after a precise duration.

Search Document