time-mocking

Time Mocking

BDD::Behave ships with Timecop-style helpers for freezing and traveling through time inside an example. They work by wrapping Raku's now term, DateTime.now, and Date.today so the wrap consults a dynamically-scoped freeze. When no freeze is active, the wrappers fall through to the real time source, so they have no effect outside a freeze block.

freeze-time

freeze-time {
  # `now`, `DateTime.now`, and `Date.today` are all frozen
  # at the moment the block began.
  my $a = now;
  sleep 0.5;
  my $b = now;
  expect($a).to.eq($b);
};

Freeze at an explicit moment with a DateTime, Instant, or ISO 8601 string:

freeze-time DateTime.new('2024-01-01T00:00:00Z'), {
  expect(DateTime.now.year).to.eq(2024);
};

freeze-time '2030-06-15T12:00:00Z', {
  expect(DateTime.now.year).to.eq(2030);
};

Date.today is also frozen to the same instant:

freeze-time DateTime.new('2024-07-04T12:00:00Z'), {
  expect(Date.today.year).to.eq(2024);
  expect(Date.today.month).to.eq(7);
  expect(Date.today.day).to.eq(4);
};

travel-to

travel-to is a synonym for the explicit-moment form of freeze-time:

travel-to DateTime.new('2026-03-15T00:00:00Z'), {
  expect(DateTime.now.year).to.eq(2026);
};

travel-by

Inside an active freeze block, travel-by advances the frozen instant forward by a Duration (or a plain Real number of seconds):

freeze-time DateTime.new('2024-01-01T00:00:00Z'), {
  expect(DateTime.now.hour).to.eq(0);

  travel-by(Duration.new(3600));

  expect(DateTime.now.hour).to.eq(1);
};

Multiple advances compose:

freeze-time $start, {
  travel-by(10);
  travel-by(20);
  travel-by(30);
  # frozen instant is now $start + 60 seconds
};

Calling travel-by outside an active freeze dies with a clear message.

Nested freezes

Inner freezes shadow outer ones. When the inner block exits, the outer freeze is restored:

freeze-time DateTime.new('2020-01-01T00:00:00Z'), {
  expect(DateTime.now.year).to.eq(2020);

  freeze-time DateTime.new('2030-06-15T00:00:00Z'), {
    expect(DateTime.now.year).to.eq(2030);
  };

  expect(DateTime.now.year).to.eq(2020);
};

Restoration

Time is always restored when a freeze block exits, including when the block throws. The frozen state never leaks to the next example.

:freeze-time metadata

Examples and groups can opt into a freeze via the :freeze-time metadata. The runner wraps the example body in a freeze around before-each / after-each hooks. Wall-clock duration is measured outside the freeze, so the example's started-at / finished-at / duration accessors still report real time.

Freeze at the moment the example body begins:

it 'freezes at start', :freeze-time, {
  my $a = now;
  sleep 0.1;
  my $b = now;
  expect($a).to.eq($b);
};

Freeze at an explicit moment:

it 'freezes at 2024-01-01', :freeze-time(DateTime.new('2024-01-01T00:00:00Z')), {
  expect(DateTime.now.year).to.eq(2024);
};

Pass :freeze-time(False) to explicitly opt out (useful when overriding an inherited group-level freeze).

current-time

current-time returns the current Instant: frozen when a freeze is active, real now otherwise. Use it from helpers that want to read "time" without bypassing the freeze.

freeze-time $instant, {
  expect(current-time()).to.eq($instant);
};

BDD::Behave v0.9.4

Behavior driven development framework

Authors

  • Greg Donald

License

Artistic-2.0

Dependencies

Provides

  • BDD::Behave
  • BDD::Behave::Benchmark
  • BDD::Behave::Benchmark::Baseline
  • BDD::Behave::Benchmark::Format
  • BDD::Behave::Bisect
  • BDD::Behave::Colors
  • BDD::Behave::Configuration
  • BDD::Behave::Coverage
  • BDD::Behave::DSL
  • BDD::Behave::Diff
  • BDD::Behave::DocExtractor
  • BDD::Behave::DryRun
  • BDD::Behave::Expectation
  • BDD::Behave::Failure
  • BDD::Behave::FailureStore
  • BDD::Behave::Failures
  • BDD::Behave::Files
  • BDD::Behave::Formatter
  • BDD::Behave::Formatter::Documentation
  • BDD::Behave::Formatter::HTML
  • BDD::Behave::Formatter::JSON
  • BDD::Behave::Formatter::JUnit
  • BDD::Behave::Formatter::JsonEvents
  • BDD::Behave::Formatter::Progress
  • BDD::Behave::Formatter::Registry
  • BDD::Behave::Formatter::TAP
  • BDD::Behave::Formatter::Tree
  • BDD::Behave::LetRuntime
  • BDD::Behave::Matcher
  • BDD::Behave::Matcher::Async
  • BDD::Behave::Matcher::Boolean
  • BDD::Behave::Matcher::Change
  • BDD::Behave::Matcher::Collection
  • BDD::Behave::Matcher::Core
  • BDD::Behave::Matcher::Custom
  • BDD::Behave::Matcher::Exception
  • BDD::Behave::Matcher::Numeric
  • BDD::Behave::Matcher::String
  • BDD::Behave::Matcher::Type
  • BDD::Behave::Mock::Allow
  • BDD::Behave::Mock::ArgMatcher
  • BDD::Behave::Mock::Double
  • BDD::Behave::Mock::HaveReceived
  • BDD::Behave::Mock::Spy
  • BDD::Behave::Mock::Stub
  • BDD::Behave::Parallel
  • BDD::Behave::Parallel::Distribution
  • BDD::Behave::Parallel::EventStream
  • BDD::Behave::Parallel::Manifest
  • BDD::Behave::Parallel::Queue
  • BDD::Behave::Parallel::WorkerPool
  • BDD::Behave::Runner
  • BDD::Behave::SharedContexts
  • BDD::Behave::SharedExamples
  • BDD::Behave::Slang
  • BDD::Behave::SpecLoader
  • BDD::Behave::SpecRegistry
  • BDD::Behave::SpecTree
  • BDD::Behave::SpecTree::Core
  • BDD::Behave::SpecTree::Example
  • BDD::Behave::SpecTree::ExampleGroup
  • BDD::Behave::SpecTree::Suite
  • BDD::Behave::Time
  • BDD::Behave::TypeName
  • BDD::Behave::Version
  • BDD::Behave::Watch
  • BDD::Behave::Watch::Session
  • BDD::Behave::Watch::SmartSelector
  • BDD::Behave::Watch::UI
  • BDD::Behave::Watch::Watcher
  • BDD::Behave::Worker

The Camelia image is copyright 2009 by Larry Wall. "Raku" is trademark of the Yet Another Society. All rights reserved.