expect

expect

expect builds an expectation about an actual value. The current matcher is be, which uses Raku's smartmatch operator (~~).

Basic form

expect(actual).to.be(expected);

expect returns a builder, .to is a no-op for readability, and .be(expected) performs the comparison.

it 'compares values', {
  expect(1 + 1).to.be(2);
  expect('hi'.uc).to.be('HI');
  expect(@list).to.be([1, 2, 3]);
}

Smartmatch semantics

Because be uses ~~ (via the built-in BeMatcher), you can match against types, regexes, ranges, junctions, and anything else Raku smartmatches:

expect(42).to.be(Int);              # type
expect('hello').to.be(/hell/);      # regex
expect(5).to.be(1..10);             # range
expect($x).to.be(any(1, 2, 3));     # junction

Plain values are wrapped in BeMatcher automatically. To plug in your own logic, pass any object that does the Matcher role, or use define-matcher for the lighter, callback-based form. Combine matchers with .and / .or. See Composable Matchers. For Raku-native any / all / one / none junctions, see Junctions.

Negation

.not flips the comparison:

expect(1 + 1).to.not.be(3);

Reading from let

expect recognises a single named-pair argument as a let lookup:

let(:answer, { 42 });

it 'matches the let', {
  expect(:answer).to.be(42);
}

You can also pass a Pair as the expected value to read from a let:

let(:answer, { 42 });

it 'compares two lets', {
  expect(:answer).to.be(:answer);
}

For other ways to read a let, see let.

Failure output

When an expectation fails, the runner records the file and line of the expect call and prints them in the failure summary so you can jump straight to the offending line.

For diffable shapes (strings, arrays, hashes, sets, bags, mixes), the failure block also includes a colorized Diff: section that highlights only the changed regions. See Diff Output for the full conventions.

Built-in matchers beyond be

expect(...).to.<matcher>(...) is the general form. The current built-ins:

MatcherPurpose
beSmartmatch (~~) against the expected value.
eqOrder-dependent structural equality via eqv. See Matchers › EqMatcher.
contain-exactlyOrder-independent multiset equality on arrays / lists. See Matchers › ContainExactlyMatcher.
match-arraySingle-array alias for contain-exactly.
includeMembership check across arrays, hashes, sets/bags, strings, and ranges. See Matchers › IncludeMatcher.
start-withSequence prefix check for arrays / lists. Per-arg prefix check for strings. See Matchers › StartWithMatcher.
end-withSequence suffix check for arrays / lists. Per-arg suffix check for strings. See Matchers › EndWithMatcher.
allEvery element of a collection must match an inner matcher. See Matchers › AllMatcher.
be-a / be-anType check including subclasses, roles, and subsets ($actual ~~ $type). See Matchers › BeAMatcher.
be-an-instance-ofStrict runtime-type check ($actual.WHAT === $type, requires defined). See Matchers › BeAnInstanceOfMatcher.
respond-toMethod-presence check via $actual.^can(...). Accepts one or more method names. See Matchers › RespondToMatcher.
have-attributesMulti-attribute check: each named pair calls the accessor on $actual and compares (eqv, or an inner Matcher). See Matchers › HaveAttributesMatcher.
be-greater-than / be-gtNumeric > comparison. Fails (not dies) on undefined or non-Real actuals. See Matchers › Comparison matchers.
be-greater-than-or-equal-to / be-gteNumeric >= comparison.
be-less-than / be-ltNumeric < comparison.
be-less-than-or-equal-to / be-lteNumeric <= comparison.
be-betweenRange check between two Real bounds. Inclusive by default. Chain .exclusive or .inclusive to flip the mode. See Matchers › BeBetweenMatcher.
be-withinTolerance check: be-within($delta).of($expected) passes when abs(actual - expected) <= delta. See Matchers › BeWithinMatcher.
be-truthyBoolean coercion check (?$actual). Empty Array/Hash, Nil, and undefined type objects are not truthy. See Matchers › BeTruthyMatcher.
be-falsyInverse of be-truthy (!$actual). See Matchers › BeFalsyMatcher.
be-nilUndefined-value check (!$actual.defined). Passes for Nil, Any, and undefined type objects. See Matchers › BeNilMatcher.
matchRegex match against a Str ($actual ~~ /pattern/). Fails (not dies) on undefined or non-Str actuals. See Matchers › MatchMatcher.
raise-errorPasses when a Callable actual raises an exception when invoked. Wrap the code under test in { ... }. Forms: raise-error, raise-error(Type), raise-error(Type, /pattern/), raise-error(/pattern/). Chain .with-message($str-or-regex) to filter by exception message (Str compares with eq, Regex with ~~). See Matchers › RaiseErrorMatcher.
changePasses when a Callable action changes the value returned by an observable block (compared with eqv). Wrap the action and the observable in { ... }. Chain .from(value) / .to(value) to constrain the start and / or end value, or .by(delta) / .by-at-least(delta) / .by-at-most(delta) for numeric deltas. See Matchers › ChangeMatcher.
be-keptPasses when a Promise actual settles in the Kept state. Blocks up to a default 5-second timeout. Pass be-kept($seconds) for a custom timeout. Surfaces the broken cause in failure messages. See Matchers › BeKeptMatcher.
be-brokenPasses when a Promise actual settles in the Broken state. Same timeout shape as be-kept. Surfaces the kept value or the broken cause in failure messages. See Matchers › BeBrokenMatcher.
complete-withinPasses when a Promise actual settles (kept or broken) within the given duration (Real seconds). See Matchers › CompleteWithinMatcher.
emitPasses when a Supply or Channel actual emits exactly the given values (compared via eqv) within the collection window. Pass :within($seconds) to change the default 1-second window. See Matchers › EmitMatcher.
emit-at-leastPasses when a Supply or Channel actual emits at least the given count of values within the collection window. Pass :within($seconds) to change the default 1-second window. See Matchers › EmitAtLeastMatcher.
completePasses when a Supply (sending done) or Channel (closed) completes within the collection window. Pass :within($seconds) to change the default 1-second window. See Matchers › CompleteMatcher.
eventuallyRe-runs a Callable actual on a polling loop until the chained inner matcher passes or the timeout elapses. Chain any matcher method (be, eq, match, include, be-truthy, be-greater-than, ...) or pass a Matcher instance via .matches-with. Configure with eventually(:timeout(s), :interval(s)) (defaults: 2s / 0.05s). Useful for eventually-consistent state. See Matchers › EventuallyMatcher.
expect([1, 2, 3]).to.eq([1, 2, 3]);
expect([1, 2, 3]).to.contain-exactly(3, 1, 2);
expect([1, 2, 3]).to.match-array([3, 1, 2]);
expect([1, 2, 3]).to.include(2);
expect({ a => 1 }).to.include(:a(1));
expect('hello').to.include('ell');
expect([1, 2, 3]).to.start-with(1, 2);
expect([1, 2, 3]).to.end-with(2, 3);
expect('hello world').to.start-with('hello');
expect('hello world').to.end-with('world');
expect([1, 2, 3]).to.all(Int);
expect(42).to.be-a(Int);
expect(42).to.be-an(Int);
expect(Dog.new).to.be-an-instance-of(Dog);
expect(Calculator.new).to.respond-to('add', 'subtract');
expect(5).to.be-between(1, 10);
expect(5).to.be-between(1, 10).exclusive;
expect(3.14e0).to.be-within(0.01e0).of(3.15e0);
expect(True).to.be-truthy;
expect(False).to.be-falsy;
expect(Nil).to.be-nil;
expect(Int).to.be-nil;          # undefined type object
expect(42).to.not.be-nil;
expect('abc123').to.match(/\d+/);
expect('HELLO').to.match(rx:i/hello/);
expect({ die "boom" }).to.raise-error;
expect({ 1 + 1 }).to.not.raise-error;
expect({ X::AdHoc.new(payload => 'oops').throw }).to.raise-error(X::AdHoc);
expect({ die "code=42" }).to.raise-error(X::AdHoc, /'code=42'/);
expect({ die "alpha" }).to.raise-error(/alpha/);
expect({ die "boom" }).to.raise-error.with-message('boom');
expect({ die "code=42" }).to.raise-error(X::AdHoc).with-message(/'code=42'/);
my $counter = 0;
expect({ $counter++ }).to.change({ $counter });
expect({ 1 + 1 }).to.not.change({ $counter });
my $balance = 0;
expect({ $balance = 100 }).to.change({ $balance }).from(0).to(100);
expect({ $balance += 50 }).to.change({ $balance }).from(100);
expect({ $balance -= 150 }).to.change({ $balance }).to(0);
expect({ $balance += 25 }).to.change({ $balance }).by(25);
expect({ $balance -= 10 }).to.change({ $balance }).by(-10);
expect({ $balance += 5 }).to.change({ $balance }).by-at-least(1).by-at-most(10);
expect(Promise.kept('done')).to.be-kept;
expect(Promise.broken('boom')).to.be-broken;
expect(start { compute() }).to.be-kept(0.5);
expect(Promise.kept('done')).to.complete-within(1);
expect(Promise.new).to.not.complete-within(0.05);
expect(Supply.from-list(1, 2, 3)).to.emit(1, 2, 3);
expect(Supply.from-list(1, 2, 3, 4)).to.emit-at-least(2);
expect(Supply.from-list(1, 2)).to.complete;
expect({ get-status() }).to.eventually.be('done');
expect({ counter() }).to.eventually(:timeout(5), :interval(0.1)).be-greater-than(100);
expect({ load() }).to.not.eventually(:timeout(0.1)).be('error');

Custom matchers

be accepts any object that does the Matcher role. The matcher's matches, failure-message, and failure-message-negated methods drive the result and the failure summary, so user-defined matchers plug in the same way as the built-in ones.

Failure behavior

When an expect(...) matcher fails, the failure is recorded on Failures.list and the example body stops executing: anything after the failing line is skipped. The example is reported once in the run summary regardless of how many expect statements its body contained.

This matches RSpec's default. Ideally each it block contains exactly one expect so the first miss is also the last. When you do need multiple expectations in a single example, wrap them in aggregate-failures { ... }. The throw is suppressed inside the block, every expectation runs, and the inner failures are rolled up into a single labeled Failures row at the aggregate-failures line. See aggregate-failures.

If you're writing meta-tests that deliberately trigger a failure and then inspect the recorded Failure records, use capture-failures { ... } instead. It suppresses the throw and returns the captured failures without polluting the surrounding example. See aggregate-failures § capture-failures for meta-tests.

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.