Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow Decimal fields traversion #74

Open
speller opened this issue Jul 12, 2023 · 1 comment
Open

Allow Decimal fields traversion #74

speller opened this issue Jul 12, 2023 · 1 comment

Comments

@speller
Copy link

speller commented Jul 12, 2023

Currently, while the Decimal has properties, they can not be traversed with foreach or any other way:

$num = new Decimal(10);
$a = [];
foreach ($num as $name => $value) {
    $a[$name] = $value;
}
print_r($a);
print_r((array)(new Decimal(10)));
print_r(get_object_vars(new Decimal(10)));

All of the code above will print empty arrays. This prevents from using PHPUnit assertions for number comparisons because PHPUnit converts objects to arrays for the detailed comparison. It is not a big issue to convert decimals to strings when we compare them directly:

$this->assertEquals('1.3', $foo->bar()->toString);

But it IS an issue when we compare objects or arrays containing decimals.

$this->assertEquals(
    new Baz(new Decimal(1), new Decimal(2)),
    $foo->bar() // returns a Baz instance { value1: Decimal(1), value2: Decimal(2) }
);

PHPUnit will detect empty objects and return true always, no matter wat are actual values in compared objects.

@speller
Copy link
Author

speller commented Jul 12, 2023

As a workaround, I created the follwing PHPUnit extension (for v9):

<?php
namespace App\Tests\Utils;

use PHPUnit\Runner\BeforeFirstTestHook;

/**
 * Register custom comparator for the Decimal class.
 */
class DecimalExtension implements BeforeFirstTestHook
{
    public function executeBeforeFirstTest(): void
    {
        \SebastianBergmann\Comparator\Factory::getInstance()->register(new DecimalComparator());
    }
}
<?php
namespace App\Tests\Utils;

use Decimal\Decimal;
use SebastianBergmann\Comparator\Comparator;
use SebastianBergmann\Comparator\ComparisonFailure;

class DecimalComparator extends Comparator
{
    public function accepts($expected, $actual)
    {
        return $expected instanceof Decimal && $actual instanceof Decimal;
    }

    /**
     * @param \Decimal\Decimal $expected
     * @param \Decimal\Decimal $actual
     * @param $delta
     * @param $canonicalize
     * @param $ignoreCase
     * @return void
     */
    public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false)
    {
        $expectedAsString = sprintf('Decimal(%s)', $expected->toString());
        $actualAsString = sprintf('Decimal(%s)', $actual->toString());

        if ($expectedAsString != $actualAsString) {
            throw new ComparisonFailure(
                $expected,
                $actual,
                $expectedAsString,
                $actualAsString,
                false,
                'Failed asserting that two Decimals are equal.'
            );
        }
    }
}

phpunit.xml:

<?xml version="1.0" encoding="UTF-8"?>

<!-- https://phpunit.readthedocs.io/en/latest/configuration.html -->
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
...
>
    <php>
        ...
    </php>

    <testsuites>
        ...
    </testsuites>

    <extensions>
        <extension class="App\Tests\Utils\DecimalExtension" />
    </extensions>
</phpunit>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant