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

assertEquals may fail if objects have hooked properties that are assigned inside the 'get' hook #6070

Open
sivlev opened this issue Dec 4, 2024 · 5 comments
Labels
feature/assertion Issues related to assertions and expectations type/change-in-php-requires-adaptation A change in PHP requires a change so that existing PHPUnit functionality continues to work

Comments

@sivlev
Copy link

sivlev commented Dec 4, 2024

I am really not sure if it is bug or reasonable behavior but just in case, I used the bug report form.

Q A
PHPUnit version 11.4.4
PHP version 8.4.1
Installation Method Composer

Summary

The (unexpected?) behavior is observed when working with a property hook, where the property is assigned inside the 'get' hook.

Current behavior

Please see the code in "How to reproduce". The test returns "Failed asserting that two objects are equal."

How to reproduce

<?php

namespace PHPMathObjects\Tests\Unit\Support;

use PHPUnit\Framework\TestCase;

class Foo {
    private(set) ?int $value = null {
        get {
            return $this->value ?? ($this->value = 1);
        }
    }

    public function flush(): void
    {
        $this->value = null;
    }
}

class FooTest extends TestCase
{
    public function testFlush(): void
    {
        $a = new Foo();
        $b = new Foo();
        $this->assertEquals($a->value, $b->value);  // OK, test passes
        $a->flush();
        $this->assertEquals($a->value, $b->value);  // OK, test passes
        $a->flush();
        $this->assertEquals($a, $b);    // Test fails. $a->value is null, $b->value is 1
    }
}

Expected behavior

I would expect that the test should pass since in real world we would never see those objects being different.

Composer info | sort

colinodell/json5                         3.0.0            UTF-8 compatible JSON5 parser for PHP
composer/pcre                            3.3.2            PCRE wrapping library that offers type-safe preg_* repla...
composer/xdebug-handler                  3.0.5            Restarts a process without Xdebug.
doctrine/annotations                     2.0.2            Docblock Annotations Parser
doctrine/lexer                           3.0.1            PHP Doctrine Lexer parser library that can be used in To...
fidry/cpu-core-counter                   1.2.0            Tiny utility to get the number of CPU cores.
infection/abstract-testframework-adapter 0.5.0            Abstract Test Framework Adapter for Infection
infection/extension-installer            0.1.2            Infection Extension Installer
infection/include-interceptor            0.2.5            Stream Wrapper: Include Interceptor. Allows to replace i...
infection/infection                      0.29.8           Infection is a Mutation Testing framework for PHP. The m...
infection/mutator                        0.4.0            Mutator interface to implement custom mutators (mutation...
justinrainbow/json-schema                5.3.0            A library to validate a json schema.
myclabs/deep-copy                        1.12.1           Create deep copies (clones) of your objects
nikic/php-parser                         5.3.1            A PHP parser written in PHP
ondram/ci-detector                       4.2.0            Detect continuous integration environment and provide un...
phar-io/manifest                         2.0.4            Component for reading phar.io manifest information from ...
phar-io/version                          3.2.1            Library for handling version information and constraints
phpbench/container                       2.2.2            Simple, configurable, service container.
phpbench/phpbench                        84.x-dev 100d9d1 PHP Benchmarking Framework
phpstan/phpstan-phpunit                  2.0.1            PHPUnit extensions and rules for PHPStan
phpstan/phpstan                          2.0.3            PHPStan - PHP Static Analysis Tool
phpunit/php-code-coverage                11.0.7           Library that provides collection, processing, and render...
phpunit/php-file-iterator                5.1.0            FilterIterator implementation that filters files based o...
phpunit/php-invoker                      5.0.1            Invoke callables with a timeout
phpunit/php-text-template                4.0.1            Simple template engine.
phpunit/php-timer                        7.0.1            Utility class for timing
phpunit/phpunit                          11.4.4           The PHP Unit Testing framework.
psr/cache                                3.0.0            Common interface for caching libraries
psr/container                            2.0.2            Common Container Interface (PHP FIG PSR-11)
psr/log                                  3.0.2            Common interface for logging libraries
sanmai/later                             0.1.4            Later: deferred wrapper object
sanmai/pipeline                          6.12             General-purpose collections pipeline
sebastian/cli-parser                     3.0.2            Library for parsing CLI options
sebastian/code-unit-reverse-lookup       4.0.1            Looks up which function or method a line of code belongs to
sebastian/code-unit                      3.0.1            Collection of value objects that represent the PHP code ...
sebastian/comparator                     6.2.1            Provides the functionality to compare PHP values for equ...
sebastian/complexity                     4.0.1            Library for calculating the complexity of PHP code units
sebastian/diff                           6.0.2            Diff implementation
sebastian/environment                    7.2.0            Provides functionality to handle HHVM/PHP environments
sebastian/exporter                       6.1.3            Provides the functionality to export PHP variables for v...
sebastian/global-state                   7.0.2            Snapshotting of global state
sebastian/lines-of-code                  3.0.1            Library for counting the lines of code in PHP source code
sebastian/object-enumerator              6.0.1            Traverses array structures and object graphs to enumerat...
sebastian/object-reflector               4.0.1            Allows reflection of object attributes, including inheri...
sebastian/recursion-context              6.0.2            Provides functionality to recursively process PHP variables
sebastian/type                           5.1.0            Collection of value objects that represent the types of ...
sebastian/version                        5.0.2            Library that helps with managing the version number of G...
seld/jsonlint                            1.11.0           JSON Linter
symfony/console                          7.2.0            Eases the creation of beautiful and testable command lin...
symfony/deprecation-contracts            3.5.1            A generic function and convention to trigger deprecation...
symfony/filesystem                       7.2.0            Provides basic utilities for the filesystem
symfony/finder                           7.2.0            Finds files and directories via an intuitive fluent inte...
symfony/options-resolver                 7.2.0            Provides an improved replacement for the array_replace P...
symfony/polyfill-ctype                   1.31.0           Symfony polyfill for ctype functions
symfony/polyfill-intl-grapheme           1.31.0           Symfony polyfill for intl's grapheme_* functions
symfony/polyfill-intl-normalizer         1.31.0           Symfony polyfill for intl's Normalizer class and related...
symfony/polyfill-mbstring                1.31.0           Symfony polyfill for the Mbstring extension
symfony/process                          7.2.0            Executes commands in sub-processes
symfony/service-contracts                3.5.1            Generic abstractions related to writing services
symfony/string                           7.2.0            Provides an object-oriented API to strings and deals wit...
thecodingmachine/safe                    2.5.0            PHP core functions that throw exceptions instead of retu...
theseer/tokenizer                        1.2.3            A small library for converting tokenized PHP source code...
webmozart/assert                         1.11.0           Assertions to validate method input/output with nice err...
webmozart/glob                           4.7.0            A PHP implementation of Ant's glob.
@sivlev sivlev added the type/bug Something is broken label Dec 4, 2024
@sebastianbergmann
Copy link
Owner

No special treatment for objects with hooked properties has been implemented. And to be honest, I would like to avoid to do so.

@sebastianbergmann sebastianbergmann added type/change-in-php-requires-adaptation A change in PHP requires a change so that existing PHPUnit functionality continues to work feature/assertion Issues related to assertions and expectations and removed type/bug Something is broken labels Dec 5, 2024
@sebastianbergmann
Copy link
Owner

@iluuu1994 @Crell What do you think?

@Crell
Copy link

Crell commented Dec 5, 2024

AIUI, assertEquals() eventually has a == at the bottom of it somewhere, I think? (I got lost trying to track down to it.) It looks like the behavior is the same for a basic == check, so it's not a PHPUnit-specific issue: https://3v4l.org/opaB9#v8.4.1

What I think is happening is that == is doing a property by property comparison, but bypassing the get hook. That... doesn't feel correct to me, but the RFC doesn't actually specify. I don't know if it's feasible or permitted to change it to invoke the get hook. @iluuu1994 ?

@iluuu1994
Copy link

iluuu1994 commented Dec 5, 2024

Internally, == should not call hooks. That would open a can of worms. I think it's absolutely reasonable to consider two objects equal if their internal representation is the same. A by-property comparison is already problematic in that regard if you have some private property that should not influence "object identity", for example one that would be used for caching. Whether PHPUnit wants to call hooks is up to you.

@sebastianbergmann
Copy link
Owner

Thank you, @iluuu1994 and @Crell.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature/assertion Issues related to assertions and expectations type/change-in-php-requires-adaptation A change in PHP requires a change so that existing PHPUnit functionality continues to work
Projects
None yet
Development

No branches or pull requests

4 participants