ahj/approval-tests

dev-main 2021-04-06 15:27 UTC

This package is auto-updated.

Last update: 2024-05-06 22:14:02 UTC


README

GitHub Build Status Scrutinizer Code Quality Scrutinizer Code Coverage Approval Tests on packagist.org

Approval Tests

Why to use Approval Tests

Given you have (possibly legacy) code,
and you want to cover it with regression tests,
and you don't want to specify the output for each input in your unit tests,
and you don't want to adjust all the tested output after you changed the logic,
then you should use Approval Tests.

Installation

composer require ahj/approval-tests --dev

Usage

Find an example on how to do Approval Testing under /tests/Example.

Create a test with phpunit, specify the input for your logic, store the output in a variable and pass it into
Approvals::create()->verifyList($input, $output):

public function testUpdateQuality(): void
{
    $input = [
        new Item('foo', 0, 1),
    ];

    $output = (new GildedRose())->updateQuality($input);

    Approvals::create()->verifyList($input, $output);
}

verifyList($input, $output) will create a map of the received data and compare it to the previous version of this data by performing an Assert::assertEquals($approved, $received).

Then run phpunit:

$ vendor/bin/phpunit tests

Then you will see a new directory in your test directory that contains two files:

/approval
  |__ approved.txt
  |__ received.txt

The approved.txt is initially empty.
In the received.txt you will find the $input mapped to the $output such as:

[foo, 0, 1] -> [foo, -1, 0]

Approve the $output by copying the content of received.txt to approved.txt or use the command from your console:

$ mv tests/Example/approval/received.txt tests/Example/approval/approved.txt

Screenshot 2021-03-27 at 09 46 18

When you run your test again, the received.txt will be gone, and you will have your test output in the approval.txt. Next you will just add more cases to your $input array in your test and approve the results. No need to specify any output manually :)

Options

Pass an empty array as $input if your logic doesn't require input:

    Approvals::create()->verifyList([], $output);

In the received.txt you will only show the $output such as:

[foo, -1, 0]

Pass $plain = true as third argument to verifyList() in order to have non formated output in the received.txt:

    Approvals::create()->verifyList([], $output, true);

Depending on the type of $output there will either be a plain or json encoded string in the received.txt. For objects or arrays it will be:

{"dirPath":"bar","fileNumber":10,"randomList":[]},{"dirPath":"bar","fileNumber":100,"randomList":[]}

For a string it will be:

+--------------+-------------+--------+--------+------+-----+------------+---------+---------+---------+---------+---------+--------+-------+
| benchmark    | subject     | groups | params | revs | its | mem_peak   | best    | mean    | mode    | worst   | stdev   | rstdev | diff  |
+--------------+-------------+--------+--------+------+-----+------------+---------+---------+---------+---------+---------+--------+-------+
| HashingBench | benchMd5    |        | []     | 1000 | 10  | 1,255,792b | 0.931μs | 0.979μs | 0.957μs | 1.153μs | 0.062μs | 6.37%  | 1.00x |
| HashingBench | benchSha1   |        | []     | 1000 | 10  | 1,255,792b | 0.988μs | 1.015μs | 1.004μs | 1.079μs | 0.026μs | 2.57%  | 1.04x |
+--------------+-------------+--------+--------+------+-----+------------+---------+---------+---------+---------+---------+--------+-------+

Testing Combinations

When to use Combinations

You have a function that takes, for example, three arguments, and you want to test its behaviour with a bunch of different values for each of those arguments.

Copy the below code or use this repo's example and adjust the number and type of inputs for your use case. Specify the input arguments of the method you want to test in $arguments: Either list the values that the arguments can take explicitly in arrays or use range(). Then pass those arguments along with an anonymous function into CombinationApprovals::create()->verifyAllCombinations()

public function testUpdateQualityWithCombinations(): void
{
    $arguments = [
        ['foo', 'bar'], # values for $name
        range(0, 3),    # values for $sellIn
        [15, 20, 25],   # values for $quantity
    ];

    CombinationApprovals::create()->verifyAllCombinations(
        function (string $name, int $sellIn, int $quantity) {
            $items = [new Item($name, $sellIn, $quantity)];
    
            return (new GildedRose())->updateQuality($items);
        },
        $arguments
    );
}

The anonymous function specifies how the input arguments should be passed into the logic that you want to test. Also, it must return the output of your tested logic, so verifyAllCombinations() can dump it into the received.txt and compare it to the latest approved.txt.

For the above example, the Approval tool would create all possible combinations of the specified input values, map those to the related output of the tested logic and dump it into the received.txt as such:

[bar, 3, 25] -> [bar, 2, 24]
[bar, 3, 20] -> [bar, 2, 19]
[bar, 3, 15] -> [bar, 2, 14]
[bar, 2, 25] -> [bar, 1, 24]
[bar, 2, 20] -> [bar, 1, 19]
[bar, 2, 15] -> [bar, 1, 14]
[bar, 1, 25] -> [bar, 0, 24]
[bar, 1, 20] -> [bar, 0, 19]
[bar, 1, 15] -> [bar, 0, 14]
[bar, 0, 25] -> [bar, -1, 23]
[bar, 0, 20] -> [bar, -1, 18]
[bar, 0, 15] -> [bar, -1, 13]
[foo, 3, 25] -> [foo, 2, 24]
[foo, 3, 20] -> [foo, 2, 19]
[foo, 3, 15] -> [foo, 2, 14]
[foo, 2, 25] -> [foo, 1, 24]
[foo, 2, 20] -> [foo, 1, 19]
[foo, 2, 15] -> [foo, 1, 14]
[foo, 1, 25] -> [foo, 0, 24]
[foo, 1, 20] -> [foo, 0, 19]
[foo, 1, 15] -> [foo, 0, 14]
[foo, 0, 25] -> [foo, -1, 23]
[foo, 0, 20] -> [foo, -1, 18]
[foo, 0, 15] -> [foo, -1, 13]

Dependencies