NAME

Test::YAFT - Yet another testing framework

SYNOPSIS

use Test::YAFT;

it q (should pass this test)
	=> got    => scalar do { ... }
	=> expect => $expected_value
	;

DISCLAIMER

Please accept the fact that I'm not English native speaker so this documentation may contain improper grammar or wording making it harder to understand.

If you encounter such place, please visit project's issue tracking.

DESCRIPTION

Test::YAFT combines features of multiple test libraries, providing a BDD-inspired, AAA-based, Context oriented testing approach.

Test Message First

The test message is always the first argument to every assumption function. This is a core design principle that ensures tests are self-documenting and readable.

it q (should increment counter)
	=> got    => $counter
	=> expect => 42
	;

All other arguments use a named parameter approach, making tests explicit and easy to understand.

AAA Pattern

Test::YAFT provides the Arrange-Act-Assert pattern:

Arrange - Set up test context using arrange { }
Act - Execute code under test using got { } or act { }
Assert - Verify results using expect or throws

BDD Inspired

The naming conventions (it, assume, there) are inspired by Behaviour-Driven Development, allowing tests to read as specifications:

it q (should return user's full name)
	=> got	  => $user->full_name
	=> expect => q (John Doe)
	;

assume q (the user's full name is correctly constructed)
	=> got	  => $user->full_name
	=> expect => q (John Doe)
	;

Test::Deep Integration

Test::YAFT uses Test::Deep under the hood, enabling powerful nested structure matching and custom comparators.

Additionally, Test::Differences is used to report differences when tests fail.

Common Assumption Arguments

got - The value under test
=> got => $value_under_test
=> got { return $value_under_test }
expect - The expected value (Test::Deep::Cmp or plain value)
=> expect => expect_foo (...)
throws - Expected exception (when testing for errors)
=> throws => expect_foo (...)
with_* - Context setup
=> with_name => $name
=> arrange { name => $name }
diag - Custom diagnostic message
=> diag => q (Custom diagnostic message)
=> diag => sub { q (Computed custom diagnostic message) }

Computed got Value

The got { } block computes the value under test within an eval, automatically checking for unexpected exceptions:

it q (should calculate total)
	=> got    { $cart->calculate_total }
	=> expect => 99.99
	;

When throws is specified, the test expects the code to die:

it q (should reject negative quantity)
	=> got    { $cart->add_item (quantity => -1) }
	=> throws => expect_obj_isa (My::X::Positive_Quantity::)
	;

To specify such block across multiple assumptions, use the act { } block.

Context

Test::YAFT uses a hierarchical context system to manage test state.

Each context enforces the following constraints:

Only one act {} block can be defined
Each property name can only be arranged once

There is one root context shared by all test files within the same process. Each subtest and each assumption creates a new sub-context.

Sub-contexts inherit values from their parent context and allow you to override them (once). Changes in a sub-context do not affect the parent or sibling contexts. Once a value has been read from a parent, it becomes immutable and can no longer be overridden.

See also: test_frame

GETTING STARTED

For a tutorial-like introduction, see Test::YAFT::Introduction.

If you are migrating from another testing library, these guides may help:

Test::YAFT for Test::Deep users
Test::YAFT for Test::Exceptions users
Test::YAFT for Test::More users
Test::YAFT for Test::Spec users
Test::YAFT for Test::Warnings users

EXPORTING

Test::YAFT exports symbols using Exporter::Tiny.

Export Tags

:all

All exported functions.

:assumptions

Assumption functions (exported by default).

:default

All functions exported by default.

:expectations

Expectation functions (exported by default).

:foundations

Foundation functions (not exported by default).

:utils

Utility functions (exported by default).

Selective Import

# Import specific functions
use Test::YAFT qw (it expect_true);

# Import only foundation functions
use Test::YAFT qw (:foundations);

# Exclude specific functions
use Test::YAFT qw (!fail);

See Exporter::Tiny for more import options.

OVERLOADED OPERATORS

Test::YAFT overloads operators for Test::Deep expectations, enabling tabular-style expectation definitions for improved readability.

!, ~, Unary -
assume q (...)
	=> expect => ! expectation
	;

assume q (...)
	=> expect => ~ expectation
	;

assume q (...)
	=> expect => - expectation
	;

Creates a complementary expectation (negation).

Binary +
assume q (user should have valid properties)
	=> expect =>
		+ expectation # must pass
		+ expectation # must pass as well
		+ expectation # must pass as well
	;

Combines multiple expectations where all must pass. Same behaviour as Test::Deep's & operator, but enables better vertical alignment for improved readability.

Binary -
assume q (...)
	=> expect =>
		+ expectation # must pass
		+ expectation # must pass as well
		- expectation # must not pass
	;

Combines expectations where - negates the expectation. Equivalent to + !.

ASSUMPTIONS

use Test::YAFT qw (:assumptions);

Assumption functions perform actual value comparison and report test results. They are exported by default.

Every assumption accepts the test message as the first positional parameter, with remaining parameters using the named approach.

When an assumption performs multiple internal expectations, it reports as a single test using the provided message, failing early.

assume

assume q (meaningful sentence)
	=> got    => ...
	=> expect => ...
	;

The primary test primitive. It creates its own sub-context so already arranged values can be redefined per assumption when needed.

Accepted parameters:

arrange
assume q (...)
	=> arrange { foo => q (bar) }
	=> arrange { bar => q (baz) }
	...
	;

<arrange { }> blocks are evaluated in the context of the assumption's sub-context before resolving the value under test.

Multiple <arrange { }> blocks are evaluated in order.

diag

Custom diagnostic message, printed on failure. When specified, no other diagnostic message is printed.

Can be a string, arrayref of strings, or coderef. Coderef receives the Test::Deep stack and value under test as parameters.

expect

Expected value. When specified along with computed got value, an additional check verifies the code did not die before comparison.

got

Value under test. When <got { }> is used, the block is evaluated and its error status is checked before comparison.

throws

Expected exception. When specified along with computed got value, an additional check verifies the code did die before comparison.

When both expect and throws are specified, throws takes precedence.

See also Test::YAFT::Test::Exception.

fail

return fail q (what failed);

return fail q (what failed)
	=> diag => q (diagnostic message)
	;

return fail q (what failed)
	=> diag => sub { q (diagnostic message) }
	;

Always fails the test. Accepts an optional diag parameter for diagnostic messages.

When diag is a coderef, it is executed and its result is passed to diag.

had_no_warnings

had_no_warnings;
had_no_warnings q (title);

Verifies that no warnings were emitted.

See also Test::Warnings::had_no_warnings.

it

it q (should be ...)
	=> got    => ...
	=> expect => ...
	;

Alias for "assume". Use when forming sentences that read naturally with "it".

nok

nok q (shouldn't be ...)
	=> got    => ...
	;

Shortcut expecting a boolean false value.

ok

ok q (should be ...)
	=> got    => ...
	;

Shortcut expecting a boolean true value.

pass

pass q (what passed);

Always passes the test.

there

there q (should be ...)
	=> got    => ...
	=> expect => ...
	;

Alias for it. Provides a convenient word for forming meaningful English sentences.

EXPECTATIONS

use Test::YAFT qw (:expectations);

Expectation functions return Test::Deep::Cmp objects describing expected values. They are exported by default.

expect_all

Re-exported from "all" in Test::Deep.

expect_any

Re-exported from "any" in Test::Deep.

expect_array

Re-exported from "array" in Test::Deep.

expect_array_each

Re-exported from "array_each" in Test::Deep.

expect_array_elements_only

Re-exported from "arrayelementsonly" in Test::Deep.

expect_array_length

Re-exported from "arraylength" in Test::Deep.

expect_array_length_only

Re-exported from "arraylengthonly" in Test::Deep.

expect_bag

Re-exported from "bag" in Test::Deep.

expect_blessed

Re-exported from "blessed" in Test::Deep.

expect_bool

Re-exported from "bool" in Test::Deep.

expect_code

Re-exported from "code" in Test::Deep.

expect_compare

expect_compare (q (<=), $max)

Similar to "cmp_ok" in Test::More but provided as an expectation, allowing combination with other Test::YAFT expectations.

expect_complement_to

expect_complement_to (42)

Negative expectation. Usually it is easier to use the overloaded complement operators ! or ~.

expect_false

Boolean expectation for false values.

expect_hash

Re-exported from "hash" in Test::Deep.

expect_hash_each

Re-exported from "hash_each" in Test::Deep.

expect_hash_keys

Re-exported from "hashkeys" in Test::Deep.

expect_hash_keys_only

Re-exported from "hashkeysonly" in Test::Deep.

expect_isa

Re-exported from "Isa" in Test::Deep. Instance or inheritance expectation.

expect_listmethods

Re-exported from "listmethods" in Test::Deep.

expect_methods

Re-exported from "methods" in Test::Deep.

expect_no_class

Re-exported from "noclass" in Test::Deep.

expect_none

Re-exported from "none" in Test::Deep.

expect_none_of

Re-exported from "noneof" in Test::Deep.

expect_num

Re-exported from "num" in Test::Deep.

expect_obj_isa

Re-exported from "obj_isa" in Test::Deep.

expect_re

Re-exported from "re" in Test::Deep.

expect_ref_type

Re-exported from "reftype" in Test::Deep.

expect_regexp_matches

Re-exported from "regexpmatches" in Test::Deep.

expect_regexp_only

Re-exported from "regexponly" in Test::Deep.

expect_regexpref

Re-exported from "regexpref" in Test::Deep.

expect_regexpref_only

Re-exported from "regexprefonly" in Test::Deep.

expect_scalarref

Re-exported from "scalarref" in Test::Deep.

expect_scalarref_only

Re-exported from "scalarrefonly" in Test::Deep.

expect_set

Re-exported from "set" in Test::Deep.

expect_shallow

Re-exported from "shallow" in Test::Deep.

expect_str

Re-exported from "str" in Test::Deep.

expect_subbag

Re-exported from "subbagof" in Test::Deep.

expect_subbag_of

Re-exported from "subbagof" in Test::Deep.

expect_subhash

Re-exported from "subhashof" in Test::Deep.

expect_subhash_of

Re-exported from "subhashof" in Test::Deep.

expect_subset

Re-exported from "subsetof" in Test::Deep.

expect_subset_of

Re-exported from "subsetof" in Test::Deep.

expect_superbag

Re-exported from "superbagof" in Test::Deep.

expect_superbag_of

Re-exported from "superbagof" in Test::Deep.

expect_superhash

Re-exported from "superhashof" in Test::Deep.

expect_superhash_of

Re-exported from "superhashof" in Test::Deep.

expect_superset

Re-exported from "supersetof" in Test::Deep.

expect_superset_of

Re-exported from "supersetof" in Test::Deep.

expect_true

Boolean expectation for true values.

expect_use_class

Re-exported from "useclass" in Test::Deep.

expect_value

=> expect => expect_value (42)

Wraps any value as an expectation.

ignore

Re-exported from "ignore" in Test::Deep.

UTILITIES

use Test::YAFT qw (:utils);

Utility functions help organise tests. They are exported by default.

act { }

act { GET qq (/countries/$_[0]) }
	q (continent)
	;

it q (should list countries)
	=> with_continent => q (europe)
	=> expect         => ...
	;

Defines a shared computation for got across multiple assumptions. The block receives dependency values as arguments.

Dependencies are specified after the block and are provided via arrange { } block or via with_NAME parameters in assumptions.

arrange { }

arrange { foo => q (bar) };

it q (should ...)
	=> arrange { foo => q (bar2) }
	=> got     { $foo->method (deduce q (foo)) }
	=> expect  => ...
	;

Sets up test context by providing values to Context::Singleton.

When called in void context, values are proclaimed immediately. When used as a parameter, values are proclaimed in the test-local frame.

Returns a guard object evaluated in the frame valid at the time of evaluation.

BAIL_OUT

Reexported "BAIL_OUT" in Test::More

diag

Re-exported from "diag" in Test::More.

done_testing

Re-exported from "done_testing" in Test::More.

explain

Re-exported from "explain" in Test::More.

got { }

it q (should die)
    => got { $foo->do_something }
    => throws => expect_obj_isa (...)
    ;

Specifies code to compute the test value. The block is evaluated in an eval, and it checks the error status before comparison.

Use with throws to test that code should die.

note

Re-exported from "note" in Test::More.

plan

Re-exported from "plan" in Test::More.

skip

Re-exported from "skip" in Test::More.

subtest

subtest q (title) => sub {
    ...;
};

Similar to "subtest" in Test::More but also creates a new Context::Singleton frame for each subtest.

todo_skip

Re-exported from "todo_skip" in Test::More.

FOUNDATIONS

use Test::YAFT qw (:foundations);

Foundation functions help build custom assumptions, expectations, and tools. They are not exported by default.

cmp_details

use Test::YAFT qw (cmp_details);
my ($ok, $stack) = cmp_details ($got, $expected);

Re-exported from "cmp_details" in Test::Deep.

deep_diag

use Test::YAFT qw (deep_diag);
deep_diag ($stack);

Re-exported from "deep_diag" in Test::Deep.

eq_deeply

use Test::YAFT qw (eq_deeply);
if (eq_deeply ($got, $expected)) {
	...
}

Re-exported from "eq_deeply" in Test::Deep.

test_deep_cmp

Creates an anonymous Test::YAFT comparator class. Returns the created class name.

sub expect_foo {
	state $class = test_deep_cmp (
		isa       => ...,
		descend   => ...,
		renderGot => ...,
	);

	$class->new (@_);
}

Accepted parameters:

isa => <PACKAGE>

Parent class, default: Test::YAFT::Cmp.

<METHOD> => <CODEREgt

Every other parameter is treated as a method to install.

test_frame (&)

use Test::YAFT qw (test_frame);
sub my_assumption {
	test_frame {
		...
	};
}

Utility for building custom assumptions. Handles:

Adjusting "level" in Test::Builder
Creating "frame" in Context::Singleton

CONTRIBUTING

Contributions are welcome!

Repository: https://github.com/happy-barney/perl-Test-YAFT
Report issues: https://github.com/happy-barney/perl-Test-YAFT/issues
Questions and ideas: https://github.com/happy-barney/perl-Test-YAFT/discussions

AUTHOR

Branislav Zahradník <barney@cpan.org>

COPYRIGHT AND LICENSE

Test::YAFT distribution is distributed under the Artistic License 2.0.