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.
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 thesave
method then the test will succeed (See the commented out line). It almost seems likewithConsecutive
is doing an identical check instead of an equals check.How to reproduce
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