sebastianbergmann / phpunit

The PHP Unit Testing framework.
https://phpunit.de/
BSD 3-Clause "New" or "Revised" License
19.69k stars 2.2k forks source link

`withConsecutive` doesn't "reset" object based parameters on each consecutive iteration #4946

Closed jimdoescode closed 2 years ago

jimdoescode commented 2 years ago
Q A
PHPUnit version 9.5.14
PHP version 8.1.4
Installation Method Composer

Summary

When mocking a class with a method that is passed an object. If that object is changed but not cloned between method calls the equalTo comparison fails despite the objects being compared seemingly being equal.

Current behavior

Running the testExecute test below will report a comparison error between two DemoObjects. However if I perform a clone on the DemoObjects before calling the save method then the test will succeed (See the commented out line). It almost seems like withConsecutive is doing an identical check instead of an equals check.

Failed asserting that two objects are equal.
--- Expected
+++ Actual
@@ @@
 Tests\DemoObject Object (
-    'field' => 'foo'
+    'field' => 'baz'
 )

How to reproduce

class DemoObject
{
    public string $field;
    public function __construct(string $field = '')
    {
        $this->field = $field;
    }
}

interface DemoRepo
{
    // Writes a DemoObject to a data store and returns success
    public function save(DemoObject $obj): bool;
}

class DemoController
{
    public function __construct(
        private DemoRepo $repo
    ) {
    }

    public function execute()
    {
        $strs = ['foo', 'bar', 'baz'];
        $obj = new DemoObject();

        // Loops over each string and sets it to the 
        // demo object then saves it off to the repository.
        foreach ($strs as $str) {
            // $obj = clone $obj; // Cloning the object will make the test work but just changing the field value fails
            $obj->field = $str;
            $this->repo->save($obj);
        }
    }
}

class TestDemoController extends \Tests\TestCase
{
    public function testExecute()
    {
        $mockRepo = $this->createMock(DemoRepo::class);
        $mockRepo->expects($this->exactly(3))
                         ->method('save')
                         ->withConsecutive(
                             [$this->equalTo(new DemoObject('foo'))],
                             [$this->equalTo(new DemoObject('bar'))],
                             [$this->equalTo(new DemoObject('baz'))],
                         )
                         ->willReturn(true);

        (new DemoController($mockRepo))->execute();
    }
}

Expected behavior

Since this is an equality comparison on objects and not an identical comparison. I would expect that changing and reusing an object in a subsequent calls would correctly compare the object with whatever the consecutive value is.

Composer Dependencies

automattic/woocommerce             3.0.0     A PHP wrapper for the WooCommerce REST API
cakephp/cache                      3.9.4     Easy to use Caching library with support for multiple caching backends
cakephp/collection                 3.9.4     Work easily with arrays and iterators by having a battery of utility traversal methods
cakephp/core                       3.9.4     CakePHP Framework Core classes
cakephp/database                   3.9.4     Flexible and powerful Database abstraction library with a familiar PDO-like API
cakephp/datasource                 3.9.4     Provides connection managing and traits for Entities and Queries that can be reused for different datastores
cakephp/log                        3.9.4     CakePHP logging library with support for multiple different streams
cakephp/utility                    3.9.4     CakePHP Utility classes such as Inflector, String, Hash, and Security
composer/ca-bundle                 1.3.1     Lets you find a path to the system CA bundle, and includes a fallback to the Mozilla CA bundle.
composer/composer                  1.10.24   Composer helps you declare, manage and install dependencies of PHP projects. It ensures you have the right stack everywhere.
composer/semver                    1.7.2     Semver library that offers utilities, version constraint parsing and validation.
composer/spdx-licenses             1.5.6     SPDX licenses list and validation library.
composer/xdebug-handler            1.4.6     Restarts a process without Xdebug.
doctrine/instantiator              1.4.0     A small, lightweight utility to instantiate objects in PHP without invoking their constructors
firebase/php-jwt                   v5.5.1    A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.
google/apiclient                   v2.12.1   Client library for Google APIs
google/apiclient-services          v0.225.0  Client library for Google APIs
google/auth                        v1.18.0   Google Auth Library for PHP
graham-campbell/result-type        v1.0.4    An Implementation Of The Result Type
guzzlehttp/guzzle                  6.5.5     Guzzle is a PHP HTTP client library
guzzlehttp/promises                1.5.1     Guzzle promises library
guzzlehttp/psr7                    1.8.3     PSR-7 message implementation that also provides common utility methods
justinrainbow/json-schema          5.2.11    A library to validate a json schema.
moneyphp/money                     v4.0.3    PHP implementation of Fowler's Money pattern
monolog/monolog                    2.3.5     Sends your logs to files, sockets, inboxes, databases and various web services
myclabs/deep-copy                  1.10.2    Create deep copies (clones) of your objects
nikic/fast-route                   v1.3.0    Fast request router for PHP
nikic/php-parser                   v4.13.2   A PHP parser written in PHP
paragonie/constant_time_encoding   v2.4.0    Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)
paragonie/random_compat            v9.99.100 PHP 5.x polyfill for random_bytes() and random_int() from PHP 7
pdepend/pdepend                    2.10.2    Official version of pdepend to be handled with Composer
phar-io/manifest                   2.0.3     Component for reading phar.io manifest information from a PHP Archive (PHAR)
phar-io/version                    3.1.0     Library for handling version information and constraints
phpdocumentor/reflection-common    2.2.0     Common reflection classes used by phpdocumentor to reflect the code structure
phpdocumentor/reflection-docblock  5.3.0     With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.
phpdocumentor/type-resolver        1.5.1     A PSR-5 based resolver of Class names, Types and Structural Element Names
phpmd/phpmd                        2.11.1    PHPMD is a spin-off project of PHP Depend and aims to be a PHP equivalent of the well known Java tool PMD.
phpoption/phpoption                1.8.1     Option Type for PHP
phpseclib/phpseclib                3.0.12    PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.
phpspec/prophecy                   v1.15.0   Highly opinionated mocking framework for PHP 5.3+
phpunit/php-code-coverage          9.2.10    Library that provides collection, processing, and rendering functionality for PHP code coverage information.
phpunit/php-file-iterator          3.0.6     FilterIterator implementation that filters files based on a list of suffixes.
phpunit/php-invoker                3.1.1     Invoke callables with a timeout
phpunit/php-text-template          2.0.4     Simple template engine.
phpunit/php-timer                  5.0.3     Utility class for timing
phpunit/phpunit                    9.5.10    The PHP Unit Testing framework.
pimple/pimple                      v3.5.0    Pimple, a simple Dependency Injection Container
psr/cache                          2.0.0     Common interface for caching libraries
psr/container                      1.1.2     Common Container Interface (PHP FIG PSR-11)
psr/http-message                   1.0.1     Common interface for HTTP messages
psr/log                            1.1.4     Common interface for logging libraries
psr/simple-cache                   1.0.1     Common interfaces for simple caching
ralouphie/getallheaders            3.0.3     A polyfill for getallheaders.
robmorgan/phinx                    0.11.7    Phinx makes it ridiculously easy to manage the database migrations for your PHP app.
rollbar/rollbar                    v2.1.0    Monitors errors and exceptions and reports them to Rollbar
sebastian/cli-parser               1.0.1     Library for parsing CLI options
sebastian/code-unit                1.0.8     Collection of value objects that represent the PHP code units
sebastian/code-unit-reverse-lookup 2.0.3     Looks up which function or method a line of code belongs to
sebastian/comparator               4.0.6     Provides the functionality to compare PHP values for equality
sebastian/complexity               2.0.2     Library for calculating the complexity of PHP code units
sebastian/diff                     4.0.4     Diff implementation
sebastian/environment              5.1.3     Provides functionality to handle HHVM/PHP environments
sebastian/exporter                 4.0.4     Provides the functionality to export PHP variables for visualization
sebastian/global-state             5.0.3     Snapshotting of global state
sebastian/lines-of-code            1.0.3     Library for counting the lines of code in PHP source code
sebastian/object-enumerator        4.0.4     Traverses array structures and object graphs to enumerate all referenced objects
sebastian/object-reflector         2.0.4     Allows reflection of object attributes, including inherited and non-public ones
sebastian/recursion-context        4.0.4     Provides functionality to recursively process PHP variables
sebastian/resource-operations      3.0.3     Provides a list of PHP built-in functions that operate on resources
sebastian/type                     2.3.4     Collection of value objects that represent the types of the PHP type system
sebastian/version                  3.0.2     Library that helps with managing the version number of Git-hosted PHP projects
seld/jsonlint                      1.8.3     JSON Linter
seld/phar-utils                    1.2.0     PHAR file format utilities, for when PHP phars you up
sendgrid/php-http-client           3.14.0    HTTP REST client, simplified for PHP
sendgrid/sendgrid                  7.11.1    This library allows you to quickly and easily send emails through Twilio SendGrid using PHP.
simplepie/simplepie                1.5.6     A simple Atom/RSS parsing library for PHP
slim/php-view                      2.2.1     Render PHP view scripts into a PSR-7 Response object.
slim/slim                          3.12.3    Slim is a PHP micro framework that helps you quickly write simple yet powerful web applications and APIs
squizlabs/php_codesniffer          3.6.2     PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.
starkbank/ecdsa                    0.0.5     fast openSSL-compatible implementation of the Elliptic Curve Digital Signature Algorithm (ECDSA)
stripe/stripe-php                  v7.116.0  Stripe PHP Library
symfony/config                     v5.4.0    Helps you find, load, combine, autofill and validate configuration values of any kind
symfony/console                    v5.4.1    Eases the creation of beautiful and testable command line interfaces
symfony/dependency-injection       v5.4.3    Allows you to standardize and centralize the way objects are constructed in your application
symfony/deprecation-contracts      v3.0.0    A generic function and convention to trigger deprecation notices
symfony/filesystem                 v5.4.0    Provides basic utilities for the filesystem
symfony/finder                     v5.4.0    Finds files and directories via an intuitive fluent interface
symfony/polyfill-ctype             v1.23.0   Symfony polyfill for ctype functions
symfony/polyfill-intl-grapheme     v1.23.1   Symfony polyfill for intl's grapheme_* functions
symfony/polyfill-intl-idn          v1.23.0   Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions
symfony/polyfill-intl-normalizer   v1.23.0   Symfony polyfill for intl's Normalizer class and related functions
symfony/polyfill-mbstring          v1.23.1   Symfony polyfill for the Mbstring extension
symfony/polyfill-php72             v1.23.0   Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions
symfony/polyfill-php73             v1.23.0   Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions
symfony/polyfill-php80             v1.23.1   Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions
symfony/polyfill-php81             v1.23.0   Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions
symfony/process                    v5.4.0    Executes commands in sub-processes
symfony/service-contracts          v2.4.1    Generic abstractions related to writing services
symfony/string                     v6.0.1    Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way
symfony/yaml                       v5.4.0    Loads and dumps YAML files
theseer/tokenizer                  1.2.1     A small library for converting tokenized PHP source code into XML and potentially other formats
twilio/sdk                         6.32.0    A PHP wrapper for Twilio's API
vlucas/phpdotenv                   v5.4.1    Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.
webmozart/assert                   1.10.0    Assertions to validate method input/output with nice error messages.
sebastianbergmann commented 2 years ago

No changes will be made to withConsecutive(), it is scheduled for deprecation and removal.

Rewrite your tests and/or your production code to not require this feature. Maybe this article can help.