diff --git a/composer.json b/composer.json index 6cfb8463d..eac8eb0d8 100644 --- a/composer.json +++ b/composer.json @@ -26,7 +26,7 @@ "phpstan/extension-installer": "^1.4.3", "phpstan/phpstan": "^2.1.36", "phpstan/phpstan-webmozart-assert": "^2.0", - "phpunit/phpunit": "^12.5", + "phpunit/phpunit": "^13.0", "rector/jack": "^0.5.1", "rector/swiss-knife": "^2.3.3", "tomasvotruba/class-leak": "^2.1" diff --git a/composer.lock b/composer.lock index 3a4e7ab96..e03667b11 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "02f9905904530e3a4e70255ba29b33fd", + "content-hash": "dce6e32d10485d72eba90cee236aeadd", "packages": [ { "name": "brick/math", @@ -7296,16 +7296,16 @@ }, { "name": "phpunit/php-code-coverage", - "version": "12.5.2", + "version": "13.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "4a9739b51cbcb355f6e95659612f92e282a7077b" + "reference": "a8b58fde2f4fbc69a064e1f80ff917607cf7737c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/4a9739b51cbcb355f6e95659612f92e282a7077b", - "reference": "4a9739b51cbcb355f6e95659612f92e282a7077b", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/a8b58fde2f4fbc69a064e1f80ff917607cf7737c", + "reference": "a8b58fde2f4fbc69a064e1f80ff917607cf7737c", "shasum": "" }, "require": { @@ -7313,17 +7313,17 @@ "ext-libxml": "*", "ext-xmlwriter": "*", "nikic/php-parser": "^5.7.0", - "php": ">=8.3", - "phpunit/php-file-iterator": "^6.0", - "phpunit/php-text-template": "^5.0", - "sebastian/complexity": "^5.0", - "sebastian/environment": "^8.0.3", - "sebastian/lines-of-code": "^4.0", - "sebastian/version": "^6.0", + "php": ">=8.4", + "phpunit/php-file-iterator": "^7.0", + "phpunit/php-text-template": "^6.0", + "sebastian/complexity": "^6.0", + "sebastian/environment": "^9.0", + "sebastian/lines-of-code": "^5.0", + "sebastian/version": "^7.0", "theseer/tokenizer": "^2.0.1" }, "require-dev": { - "phpunit/phpunit": "^12.5.1" + "phpunit/phpunit": "^13.0" }, "suggest": { "ext-pcov": "PHP extension that provides line coverage", @@ -7332,7 +7332,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "12.5.x-dev" + "dev-main": "13.0.x-dev" } }, "autoload": { @@ -7361,7 +7361,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/12.5.2" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/13.0.1" }, "funding": [ { @@ -7381,32 +7381,32 @@ "type": "tidelift" } ], - "time": "2025-12-24T07:03:04+00:00" + "time": "2026-02-06T06:05:15+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "6.0.1", + "version": "7.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "3d1cd096ef6bea4bf2762ba586e35dbd317cbfd5" + "reference": "6e5aa1fb0a95b1703d83e721299ee18bb4e2de50" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/3d1cd096ef6bea4bf2762ba586e35dbd317cbfd5", - "reference": "3d1cd096ef6bea4bf2762ba586e35dbd317cbfd5", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/6e5aa1fb0a95b1703d83e721299ee18bb4e2de50", + "reference": "6e5aa1fb0a95b1703d83e721299ee18bb4e2de50", "shasum": "" }, "require": { - "php": ">=8.3" + "php": ">=8.4" }, "require-dev": { - "phpunit/phpunit": "^12.0" + "phpunit/phpunit": "^13.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "6.0-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -7434,7 +7434,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/6.0.1" + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/7.0.0" }, "funding": [ { @@ -7454,28 +7454,28 @@ "type": "tidelift" } ], - "time": "2026-02-02T14:04:18+00:00" + "time": "2026-02-06T04:33:26+00:00" }, { "name": "phpunit/php-invoker", - "version": "6.0.0", + "version": "7.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-invoker.git", - "reference": "12b54e689b07a25a9b41e57736dfab6ec9ae5406" + "reference": "42e5c5cae0c65df12d1b1a3ab52bf3f50f244d88" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/12b54e689b07a25a9b41e57736dfab6ec9ae5406", - "reference": "12b54e689b07a25a9b41e57736dfab6ec9ae5406", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/42e5c5cae0c65df12d1b1a3ab52bf3f50f244d88", + "reference": "42e5c5cae0c65df12d1b1a3ab52bf3f50f244d88", "shasum": "" }, "require": { - "php": ">=8.3" + "php": ">=8.4" }, "require-dev": { "ext-pcntl": "*", - "phpunit/phpunit": "^12.0" + "phpunit/phpunit": "^13.0" }, "suggest": { "ext-pcntl": "*" @@ -7483,7 +7483,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "6.0-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -7510,40 +7510,52 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-invoker/issues", "security": "https://github.com/sebastianbergmann/php-invoker/security/policy", - "source": "https://github.com/sebastianbergmann/php-invoker/tree/6.0.0" + "source": "https://github.com/sebastianbergmann/php-invoker/tree/7.0.0" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-invoker", + "type": "tidelift" } ], - "time": "2025-02-07T04:58:58+00:00" + "time": "2026-02-06T04:34:47+00:00" }, { "name": "phpunit/php-text-template", - "version": "5.0.0", + "version": "6.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "e1367a453f0eda562eedb4f659e13aa900d66c53" + "reference": "a47af19f93f76aa3368303d752aa5272ca3299f4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/e1367a453f0eda562eedb4f659e13aa900d66c53", - "reference": "e1367a453f0eda562eedb4f659e13aa900d66c53", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/a47af19f93f76aa3368303d752aa5272ca3299f4", + "reference": "a47af19f93f76aa3368303d752aa5272ca3299f4", "shasum": "" }, "require": { - "php": ">=8.3" + "php": ">=8.4" }, "require-dev": { - "phpunit/phpunit": "^12.0" + "phpunit/phpunit": "^13.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -7570,40 +7582,52 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-text-template/issues", "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/5.0.0" + "source": "https://github.com/sebastianbergmann/php-text-template/tree/6.0.0" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-text-template", + "type": "tidelift" } ], - "time": "2025-02-07T04:59:16+00:00" + "time": "2026-02-06T04:36:37+00:00" }, { "name": "phpunit/php-timer", - "version": "8.0.0", + "version": "9.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc" + "reference": "a0e12065831f6ab0d83120dc61513eb8d9a966f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc", - "reference": "f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/a0e12065831f6ab0d83120dc61513eb8d9a966f6", + "reference": "a0e12065831f6ab0d83120dc61513eb8d9a966f6", "shasum": "" }, "require": { - "php": ">=8.3" + "php": ">=8.4" }, "require-dev": { - "phpunit/phpunit": "^12.0" + "phpunit/phpunit": "^13.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "8.0-dev" + "dev-main": "9.0-dev" } }, "autoload": { @@ -7630,28 +7654,40 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-timer/issues", "security": "https://github.com/sebastianbergmann/php-timer/security/policy", - "source": "https://github.com/sebastianbergmann/php-timer/tree/8.0.0" + "source": "https://github.com/sebastianbergmann/php-timer/tree/9.0.0" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-timer", + "type": "tidelift" } ], - "time": "2025-02-07T04:59:38+00:00" + "time": "2026-02-06T04:37:53+00:00" }, { "name": "phpunit/phpunit", - "version": "12.5.9", + "version": "13.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "83d4c158526c879b4c5cf7149d27958b6d912373" + "reference": "e045b31f855d4511a2091aa707a2bb0bb5456632" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/83d4c158526c879b4c5cf7149d27958b6d912373", - "reference": "83d4c158526c879b4c5cf7149d27958b6d912373", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/e045b31f855d4511a2091aa707a2bb0bb5456632", + "reference": "e045b31f855d4511a2091aa707a2bb0bb5456632", "shasum": "" }, "require": { @@ -7664,22 +7700,22 @@ "myclabs/deep-copy": "^1.13.4", "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", - "php": ">=8.3", - "phpunit/php-code-coverage": "^12.5.2", - "phpunit/php-file-iterator": "^6.0.1", - "phpunit/php-invoker": "^6.0.0", - "phpunit/php-text-template": "^5.0.0", - "phpunit/php-timer": "^8.0.0", - "sebastian/cli-parser": "^4.2.0", - "sebastian/comparator": "^7.1.4", - "sebastian/diff": "^7.0.0", - "sebastian/environment": "^8.0.3", - "sebastian/exporter": "^7.0.2", - "sebastian/global-state": "^8.0.2", - "sebastian/object-enumerator": "^7.0.0", - "sebastian/recursion-context": "^7.0.1", - "sebastian/type": "^6.0.3", - "sebastian/version": "^6.0.0", + "php": ">=8.4.1", + "phpunit/php-code-coverage": "^13.0.0", + "phpunit/php-file-iterator": "^7.0.0", + "phpunit/php-invoker": "^7.0.0", + "phpunit/php-text-template": "^6.0.0", + "phpunit/php-timer": "^9.0.0", + "sebastian/cli-parser": "^5.0.0", + "sebastian/comparator": "^8.0.0", + "sebastian/diff": "^8.0.0", + "sebastian/environment": "^9.0.0", + "sebastian/exporter": "^8.0.0", + "sebastian/global-state": "^9.0.0", + "sebastian/object-enumerator": "^8.0.0", + "sebastian/recursion-context": "^8.0.0", + "sebastian/type": "^7.0.0", + "sebastian/version": "^7.0.0", "staabm/side-effects-detector": "^1.0.5" }, "bin": [ @@ -7688,7 +7724,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "12.5-dev" + "dev-main": "13.0-dev" } }, "autoload": { @@ -7720,7 +7756,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/12.5.9" + "source": "https://github.com/sebastianbergmann/phpunit/tree/13.0.0" }, "funding": [ { @@ -7744,7 +7780,7 @@ "type": "tidelift" } ], - "time": "2026-02-05T08:01:09+00:00" + "time": "2026-02-06T04:57:13+00:00" }, { "name": "rector/jack", @@ -7842,28 +7878,28 @@ }, { "name": "sebastian/cli-parser", - "version": "4.2.0", + "version": "5.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "90f41072d220e5c40df6e8635f5dafba2d9d4d04" + "reference": "48a4654fa5e48c1c81214e9930048a572d4b23ca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/90f41072d220e5c40df6e8635f5dafba2d9d4d04", - "reference": "90f41072d220e5c40df6e8635f5dafba2d9d4d04", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/48a4654fa5e48c1c81214e9930048a572d4b23ca", + "reference": "48a4654fa5e48c1c81214e9930048a572d4b23ca", "shasum": "" }, "require": { - "php": ">=8.3" + "php": ">=8.4" }, "require-dev": { - "phpunit/phpunit": "^12.0" + "phpunit/phpunit": "^13.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "4.2-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -7887,7 +7923,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/cli-parser/issues", "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/4.2.0" + "source": "https://github.com/sebastianbergmann/cli-parser/tree/5.0.0" }, "funding": [ { @@ -7907,31 +7943,31 @@ "type": "tidelift" } ], - "time": "2025-09-14T09:36:45+00:00" + "time": "2026-02-06T04:39:44+00:00" }, { "name": "sebastian/comparator", - "version": "7.1.4", + "version": "8.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "6a7de5df2e094f9a80b40a522391a7e6022df5f6" + "reference": "29b232ddc29c2b114c0358c69b3084e7c3da0d58" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/6a7de5df2e094f9a80b40a522391a7e6022df5f6", - "reference": "6a7de5df2e094f9a80b40a522391a7e6022df5f6", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/29b232ddc29c2b114c0358c69b3084e7c3da0d58", + "reference": "29b232ddc29c2b114c0358c69b3084e7c3da0d58", "shasum": "" }, "require": { "ext-dom": "*", "ext-mbstring": "*", - "php": ">=8.3", - "sebastian/diff": "^7.0", - "sebastian/exporter": "^7.0" + "php": ">=8.4", + "sebastian/diff": "^8.0", + "sebastian/exporter": "^8.0" }, "require-dev": { - "phpunit/phpunit": "^12.2" + "phpunit/phpunit": "^13.0" }, "suggest": { "ext-bcmath": "For comparing BcMath\\Number objects" @@ -7939,7 +7975,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "7.1-dev" + "dev-main": "8.0-dev" } }, "autoload": { @@ -7979,7 +8015,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", "security": "https://github.com/sebastianbergmann/comparator/security/policy", - "source": "https://github.com/sebastianbergmann/comparator/tree/7.1.4" + "source": "https://github.com/sebastianbergmann/comparator/tree/8.0.0" }, "funding": [ { @@ -7999,33 +8035,33 @@ "type": "tidelift" } ], - "time": "2026-01-24T09:28:48+00:00" + "time": "2026-02-06T04:40:39+00:00" }, { "name": "sebastian/complexity", - "version": "5.0.0", + "version": "6.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "bad4316aba5303d0221f43f8cee37eb58d384bbb" + "reference": "c5651c795c98093480df79350cb050813fc7a2f3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/bad4316aba5303d0221f43f8cee37eb58d384bbb", - "reference": "bad4316aba5303d0221f43f8cee37eb58d384bbb", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/c5651c795c98093480df79350cb050813fc7a2f3", + "reference": "c5651c795c98093480df79350cb050813fc7a2f3", "shasum": "" }, "require": { "nikic/php-parser": "^5.0", - "php": ">=8.3" + "php": ">=8.4" }, "require-dev": { - "phpunit/phpunit": "^12.0" + "phpunit/phpunit": "^13.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -8049,41 +8085,53 @@ "support": { "issues": "https://github.com/sebastianbergmann/complexity/issues", "security": "https://github.com/sebastianbergmann/complexity/security/policy", - "source": "https://github.com/sebastianbergmann/complexity/tree/5.0.0" + "source": "https://github.com/sebastianbergmann/complexity/tree/6.0.0" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/complexity", + "type": "tidelift" } ], - "time": "2025-02-07T04:55:25+00:00" + "time": "2026-02-06T04:41:32+00:00" }, { "name": "sebastian/diff", - "version": "7.0.0", + "version": "8.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "7ab1ea946c012266ca32390913653d844ecd085f" + "reference": "a2b6d09d7729ee87d605a439469f9dcc39be5ea3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7ab1ea946c012266ca32390913653d844ecd085f", - "reference": "7ab1ea946c012266ca32390913653d844ecd085f", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/a2b6d09d7729ee87d605a439469f9dcc39be5ea3", + "reference": "a2b6d09d7729ee87d605a439469f9dcc39be5ea3", "shasum": "" }, "require": { - "php": ">=8.3" + "php": ">=8.4" }, "require-dev": { - "phpunit/phpunit": "^12.0", + "phpunit/phpunit": "^13.0", "symfony/process": "^7.2" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "7.0-dev" + "dev-main": "8.0-dev" } }, "autoload": { @@ -8116,35 +8164,47 @@ "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", "security": "https://github.com/sebastianbergmann/diff/security/policy", - "source": "https://github.com/sebastianbergmann/diff/tree/7.0.0" + "source": "https://github.com/sebastianbergmann/diff/tree/8.0.0" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/diff", + "type": "tidelift" } ], - "time": "2025-02-07T04:55:46+00:00" + "time": "2026-02-06T04:42:27+00:00" }, { "name": "sebastian/environment", - "version": "8.0.3", + "version": "9.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "24a711b5c916efc6d6e62aa65aa2ec98fef77f68" + "reference": "bb64d08145b021b67d5f253308a498b73ab0461e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/24a711b5c916efc6d6e62aa65aa2ec98fef77f68", - "reference": "24a711b5c916efc6d6e62aa65aa2ec98fef77f68", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/bb64d08145b021b67d5f253308a498b73ab0461e", + "reference": "bb64d08145b021b67d5f253308a498b73ab0461e", "shasum": "" }, "require": { - "php": ">=8.3" + "php": ">=8.4" }, "require-dev": { - "phpunit/phpunit": "^12.0" + "phpunit/phpunit": "^13.0" }, "suggest": { "ext-posix": "*" @@ -8152,7 +8212,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "8.0-dev" + "dev-main": "9.0-dev" } }, "autoload": { @@ -8180,7 +8240,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", "security": "https://github.com/sebastianbergmann/environment/security/policy", - "source": "https://github.com/sebastianbergmann/environment/tree/8.0.3" + "source": "https://github.com/sebastianbergmann/environment/tree/9.0.0" }, "funding": [ { @@ -8200,34 +8260,34 @@ "type": "tidelift" } ], - "time": "2025-08-12T14:11:56+00:00" + "time": "2026-02-06T04:43:29+00:00" }, { "name": "sebastian/exporter", - "version": "7.0.2", + "version": "8.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "016951ae10980765e4e7aee491eb288c64e505b7" + "reference": "dc31f1f8e0186c8f0bb3e48fd4d51421d8905fea" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/016951ae10980765e4e7aee491eb288c64e505b7", - "reference": "016951ae10980765e4e7aee491eb288c64e505b7", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/dc31f1f8e0186c8f0bb3e48fd4d51421d8905fea", + "reference": "dc31f1f8e0186c8f0bb3e48fd4d51421d8905fea", "shasum": "" }, "require": { "ext-mbstring": "*", - "php": ">=8.3", - "sebastian/recursion-context": "^7.0" + "php": ">=8.4", + "sebastian/recursion-context": "^8.0" }, "require-dev": { - "phpunit/phpunit": "^12.0" + "phpunit/phpunit": "^13.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "7.0-dev" + "dev-main": "8.0-dev" } }, "autoload": { @@ -8270,7 +8330,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", "security": "https://github.com/sebastianbergmann/exporter/security/policy", - "source": "https://github.com/sebastianbergmann/exporter/tree/7.0.2" + "source": "https://github.com/sebastianbergmann/exporter/tree/8.0.0" }, "funding": [ { @@ -8290,35 +8350,35 @@ "type": "tidelift" } ], - "time": "2025-09-24T06:16:11+00:00" + "time": "2026-02-06T04:44:28+00:00" }, { "name": "sebastian/global-state", - "version": "8.0.2", + "version": "9.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "ef1377171613d09edd25b7816f05be8313f9115d" + "reference": "e52e3dc22441e6218c710afe72c3042f8fc41ea7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/ef1377171613d09edd25b7816f05be8313f9115d", - "reference": "ef1377171613d09edd25b7816f05be8313f9115d", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e52e3dc22441e6218c710afe72c3042f8fc41ea7", + "reference": "e52e3dc22441e6218c710afe72c3042f8fc41ea7", "shasum": "" }, "require": { - "php": ">=8.3", - "sebastian/object-reflector": "^5.0", - "sebastian/recursion-context": "^7.0" + "php": ">=8.4", + "sebastian/object-reflector": "^6.0", + "sebastian/recursion-context": "^8.0" }, "require-dev": { "ext-dom": "*", - "phpunit/phpunit": "^12.0" + "phpunit/phpunit": "^13.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "8.0-dev" + "dev-main": "9.0-dev" } }, "autoload": { @@ -8344,7 +8404,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", "security": "https://github.com/sebastianbergmann/global-state/security/policy", - "source": "https://github.com/sebastianbergmann/global-state/tree/8.0.2" + "source": "https://github.com/sebastianbergmann/global-state/tree/9.0.0" }, "funding": [ { @@ -8364,33 +8424,33 @@ "type": "tidelift" } ], - "time": "2025-08-29T11:29:25+00:00" + "time": "2026-02-06T04:45:13+00:00" }, { "name": "sebastian/lines-of-code", - "version": "4.0.0", + "version": "5.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "97ffee3bcfb5805568d6af7f0f893678fc076d2f" + "reference": "4f21bb7768e1c997722ccc7efb1d6b5c11bfd471" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/97ffee3bcfb5805568d6af7f0f893678fc076d2f", - "reference": "97ffee3bcfb5805568d6af7f0f893678fc076d2f", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/4f21bb7768e1c997722ccc7efb1d6b5c11bfd471", + "reference": "4f21bb7768e1c997722ccc7efb1d6b5c11bfd471", "shasum": "" }, "require": { "nikic/php-parser": "^5.0", - "php": ">=8.3" + "php": ">=8.4" }, "require-dev": { - "phpunit/phpunit": "^12.0" + "phpunit/phpunit": "^13.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "4.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -8414,42 +8474,54 @@ "support": { "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/4.0.0" + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/5.0.0" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/lines-of-code", + "type": "tidelift" } ], - "time": "2025-02-07T04:57:28+00:00" + "time": "2026-02-06T04:45:54+00:00" }, { "name": "sebastian/object-enumerator", - "version": "7.0.0", + "version": "8.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "1effe8e9b8e068e9ae228e542d5d11b5d16db894" + "reference": "b39ab125fd9a7434b0ecbc4202eebce11a98cfc5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/1effe8e9b8e068e9ae228e542d5d11b5d16db894", - "reference": "1effe8e9b8e068e9ae228e542d5d11b5d16db894", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/b39ab125fd9a7434b0ecbc4202eebce11a98cfc5", + "reference": "b39ab125fd9a7434b0ecbc4202eebce11a98cfc5", "shasum": "" }, "require": { - "php": ">=8.3", - "sebastian/object-reflector": "^5.0", - "sebastian/recursion-context": "^7.0" + "php": ">=8.4", + "sebastian/object-reflector": "^6.0", + "sebastian/recursion-context": "^8.0" }, "require-dev": { - "phpunit/phpunit": "^12.0" + "phpunit/phpunit": "^13.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "7.0-dev" + "dev-main": "8.0-dev" } }, "autoload": { @@ -8472,40 +8544,52 @@ "support": { "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/7.0.0" + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/8.0.0" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/object-enumerator", + "type": "tidelift" } ], - "time": "2025-02-07T04:57:48+00:00" + "time": "2026-02-06T04:46:36+00:00" }, { "name": "sebastian/object-reflector", - "version": "5.0.0", + "version": "6.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "4bfa827c969c98be1e527abd576533293c634f6a" + "reference": "3ca042c2c60b0eab094f8a1b6a7093f4d4c72200" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/4bfa827c969c98be1e527abd576533293c634f6a", - "reference": "4bfa827c969c98be1e527abd576533293c634f6a", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/3ca042c2c60b0eab094f8a1b6a7093f4d4c72200", + "reference": "3ca042c2c60b0eab094f8a1b6a7093f4d4c72200", "shasum": "" }, "require": { - "php": ">=8.3" + "php": ">=8.4" }, "require-dev": { - "phpunit/phpunit": "^12.0" + "phpunit/phpunit": "^13.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -8528,40 +8612,52 @@ "support": { "issues": "https://github.com/sebastianbergmann/object-reflector/issues", "security": "https://github.com/sebastianbergmann/object-reflector/security/policy", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/5.0.0" + "source": "https://github.com/sebastianbergmann/object-reflector/tree/6.0.0" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/object-reflector", + "type": "tidelift" } ], - "time": "2025-02-07T04:58:17+00:00" + "time": "2026-02-06T04:47:13+00:00" }, { "name": "sebastian/recursion-context", - "version": "7.0.1", + "version": "8.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "0b01998a7d5b1f122911a66bebcb8d46f0c82d8c" + "reference": "74c5af21f6a5833e91767ca068c4d3dfec15317e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/0b01998a7d5b1f122911a66bebcb8d46f0c82d8c", - "reference": "0b01998a7d5b1f122911a66bebcb8d46f0c82d8c", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/74c5af21f6a5833e91767ca068c4d3dfec15317e", + "reference": "74c5af21f6a5833e91767ca068c4d3dfec15317e", "shasum": "" }, "require": { - "php": ">=8.3" + "php": ">=8.4" }, "require-dev": { - "phpunit/phpunit": "^12.0" + "phpunit/phpunit": "^13.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "7.0-dev" + "dev-main": "8.0-dev" } }, "autoload": { @@ -8592,7 +8688,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/7.0.1" + "source": "https://github.com/sebastianbergmann/recursion-context/tree/8.0.0" }, "funding": [ { @@ -8612,32 +8708,32 @@ "type": "tidelift" } ], - "time": "2025-08-13T04:44:59+00:00" + "time": "2026-02-06T04:51:28+00:00" }, { "name": "sebastian/type", - "version": "6.0.3", + "version": "7.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "e549163b9760b8f71f191651d22acf32d56d6d4d" + "reference": "42412224607bd3931241bbd17f38e0f972f5a916" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/e549163b9760b8f71f191651d22acf32d56d6d4d", - "reference": "e549163b9760b8f71f191651d22acf32d56d6d4d", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/42412224607bd3931241bbd17f38e0f972f5a916", + "reference": "42412224607bd3931241bbd17f38e0f972f5a916", "shasum": "" }, "require": { - "php": ">=8.3" + "php": ">=8.4" }, "require-dev": { - "phpunit/phpunit": "^12.0" + "phpunit/phpunit": "^13.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "6.0-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -8661,7 +8757,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/type/issues", "security": "https://github.com/sebastianbergmann/type/security/policy", - "source": "https://github.com/sebastianbergmann/type/tree/6.0.3" + "source": "https://github.com/sebastianbergmann/type/tree/7.0.0" }, "funding": [ { @@ -8681,29 +8777,29 @@ "type": "tidelift" } ], - "time": "2025-08-09T06:57:12+00:00" + "time": "2026-02-06T04:52:09+00:00" }, { "name": "sebastian/version", - "version": "6.0.0", + "version": "7.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/version.git", - "reference": "3e6ccf7657d4f0a59200564b08cead899313b53c" + "reference": "ad37a5552c8e2b88572249fdc19b6da7792e021b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/3e6ccf7657d4f0a59200564b08cead899313b53c", - "reference": "3e6ccf7657d4f0a59200564b08cead899313b53c", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/ad37a5552c8e2b88572249fdc19b6da7792e021b", + "reference": "ad37a5552c8e2b88572249fdc19b6da7792e021b", "shasum": "" }, "require": { - "php": ">=8.3" + "php": ">=8.4" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "6.0-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -8727,15 +8823,27 @@ "support": { "issues": "https://github.com/sebastianbergmann/version/issues", "security": "https://github.com/sebastianbergmann/version/security/policy", - "source": "https://github.com/sebastianbergmann/version/tree/6.0.0" + "source": "https://github.com/sebastianbergmann/version/tree/7.0.0" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/version", + "type": "tidelift" } ], - "time": "2025-02-07T05:00:38+00:00" + "time": "2026-02-06T04:52:52+00:00" }, { "name": "staabm/side-effects-detector", diff --git a/public/assets/images/blog/2026/phpunit-notices-spam.png b/public/assets/images/blog/2026/phpunit-notices-spam.png new file mode 100644 index 000000000..32a898806 Binary files /dev/null and b/public/assets/images/blog/2026/phpunit-notices-spam.png differ diff --git a/rector.php b/rector.php index 3569811c9..d6feedb7b 100644 --- a/rector.php +++ b/rector.php @@ -19,6 +19,7 @@ privatization: true, naming: true, rectorPreset: true, + phpunitCodeQuality: true ) ->withPhpSets() ->withAttributesSets() diff --git a/resources/blog/posts/2026/2026-02-07-upgrade-to-phpunit-125-in-7-diffs.md b/resources/blog/posts/2026/2026-02-07-upgrade-to-phpunit-125-in-7-diffs.md new file mode 100644 index 000000000..f72cdb423 --- /dev/null +++ b/resources/blog/posts/2026/2026-02-07-upgrade-to-phpunit-125-in-7-diffs.md @@ -0,0 +1,465 @@ +--- +id: 83 +title: "Upgrade to PHPUnit 12.5 in 7 Diffs" +perex: | + PHPUnit 12 was released a year ago, but only PHPUnit 12.5 released in December 2025 includes valuable features that are worth it. + + The most important change, that will affect your code, is that mocks are now much stricter. There are also stubs, a mock that does nothing. How do you spot them and separate them? + + Curious how to get from 4000 notices to under 100 in 7 diffs? Read on. +--- + +What is the difference between a mock and a stub? You did not have to care until PHPUnit 12.5, but now you do. + +Why? Because PHPUnit now complains about their misuse very verbosely. There is no way to ignore it: + + + +
+
+ +There is more precise definition in the PHPUnit docs, but in plain English: + +
+"What is a difference between a mock and a stub?" +
+ +
+ +* **A mock** is a fake class that has expectations about being called or not being called, + +```php +$someMock = $this->createMock(SomeClass::class); +$someMock->expects($this->once()) + ->method('someMethod') + ->willReturn(100); +``` + +Here we expect the `someMethod` to be called. PHPUnit will crash with error otherwise. + +
+ +* **A stub** is also a fake class, but it does not do anything at all. + +We can use it to make comply with constructor requirements: + +```php +$someClass = new SomeClass($this->createStub(SomeDependency::class)); +``` + +
+ +We can also use it to assert the same object is used on a getter call later: + +```php +$request = $this->createStub(Request::class); +$requestStack = new RequestStack($request); + +$this->assertSame($request, $requestStack->getCurrentRequest()); +``` + +
+ +This leads us to the first and simplest change we can make. + +
+ +## 1. Use `createStub()` instead of `createMock()` in arguments + +The first cases are as simple as: + +```diff + $someClass = new SomeClass( +- $this->createMock(SomeDependency::class) ++ $this->createStub(SomeDependency::class) + ); +``` + +
+ +Also variable assigns: + +```diff +-$someDependency = $this->createMock(SomeDependency::class); ++$someDependency = $this->createStub(SomeDependency::class); + + $someClass = new SomeClass($someDependency); +``` + +Or coalesce directly in the argument: + +```diff +-$someClass = new SomeClass( + $someInput ?? $this->createMock(SomeDependency::class), + $someInput ?? $this->createStub(SomeDependency::class), + ); +``` + +
+ +But also property fetches without any expectations: + +```diff + protected function setUp() + { +- $this->someDependency = $this->createMock(SomeDependency::class); ++ $this->someDependency = $this->createStub(SomeDependency::class); + } + + + public function test() + { + $someClass = new SomeClass($this->someDependency); + } +``` + +
+ +## 2. Inline once-used Mocks Property to a Variable + +This is not a change in PHPUnit 12.5 itself, but it helps with the changes that come with it. During the upgrade, I've noticed some properties are used just once. + +Properties are not variables for one reason: to be used across multiple methods. Let us fix that: + +```diff +-private MockObject $someDependency; + + protected function setUp() + { +- $this->someDependency = $this->createMock(SomeDependency::class); + } + + + public function test() + { +- $someClass = new SomeClass($this->someDependency); ++ $someClass = new SomeClass($this->createStub(SomeDependency::class)); + } +``` + +We have less code to read for us and GPT, and also can move to `createStub()` without any doubts. + +
+ +
+ +## 3. Remove never used isolated mocks and dead code + +Speaking of dead code, the mocks to stub narrowign also surfaces another issue: never used mocks that live on their own island. + +
+ +PHPUnit being more stricter now helps us find code that was never evaluated and only taking our reading space. + +```php +$this->createMock(SomeClass::class) + ->method('someMethod') + ->with($this->isInstanceOf(InputArgument::class)) + ->willReturn(100); +``` + +What is wrong with this code snippet, apart from being a stub? It is never used. We created it, but we never assigned it to a variable, nor property feath, nor argument of a method call. + +It is dead code. Remove it: + +```diff +-$this->createMock(SomeClass::class) +- ->method('someMethod') +- ->with($this->isInstanceOf(InputArgument::class)) +- ->willReturn(100); +``` + +
+ +Beware, this can be as complex as a well defined and typed property... that is never used. Dead code, remove it: + +```diff +-private MockObject $mockProperty; + + protected function setUp(): void + { +- $this->mockProperty = $this->createMock(\stdClass::class); +- $this->mockProperty->expects($this->once()) +- ->method('someMethod') +- ->willReturn('someValue'); + } +``` + +
+ +## 4. From `$this->any()` to explicit expectations + +PHPUnit now also deprecated used of `$this->any()` expectations. This is a wise choice, as it effectively says "we expect 0, 1, or any number of occurrences". This code as well could be removed. + +
+ +Following code snippets have the same meaning: + +```php +$someClass = $this->createMock(SomeClass::class); + +$someClass->expects($this->any()) + ->method('someMethod') + ->willReturn(100); + +// same as +$someClass + ->method('someMethod') + ->willReturn(100); +``` + +Both will be most reported by PHPUnit as stubs. They have 0 expectations (among other numbers). So how do we fix that? Change we used before is not enough and will not work here: + +```diff +-$someClass = $this->createMock(SomeClass::class); ++$someClass = $this->createStub(SomeClass::class); +``` + +
+ +We have to be honest here, and it might require to understand the code. + +* Is it a dummy method defined in `setUp()` method, in case it will be called any further in the codebase? +* Is it implicit `$this->any()`, just because we forgot to add explicit number? + +
+ +The most common case in codebases I work with was the second one: + +```diff +-$someClass->expects($this->any()) ++$someClass->expects($this->atLeastOnce()) + ->method('someMethod') + ->willReturn(100); +``` + +But what about the `setUp()` method? Do we have to now go through all the code and inline all the properties? This hurts just writing it. This gets us to our next change: + +
+ + +## 5. Add `#[AllowMockObjectsWithoutExpectations]` for optional setUp mocks + +It's perfectly reasonable to use `setUp()` method to create mock properties that may or may not be used in one of the test method later: + +```php +private SomeObject $someDependency; + +protected function setUp(): void +{ + $this->someDependency = $this->createMock(SomeDependency::class) + // implicit ->expects($this->any()) + ->method('someMethod') + ->willReturn(100); +} + + +public function testUsing() +{ + $someClass = new SomeClass($this->someDependency); + // ... +} + +public function testNotUsing() +{ + $someClass = new SomeClass(new AnotherDependnecy()); + // ... +} +``` + +Here we have one mocked object as a property with *any* expectations. Then there are two test methods. The first one uses the mock as a mock. +The second test method does not, so from its point of view it is a stub. + +(Also, another method can be using the property, but never calling the mocked method, so it's a stub as well). + +
+ +An attribute to the rescue! + +```diff + use PHPUnit\Framework\TestCase; ++use PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations; + ++#[AllowMockObjectsWithoutExpectations] + final class SomeTest extends TestCase + { + private SomeObject $someDependency; + + // ... + } +``` + +This attribute will silence the notices about stubs in this test class. + +
+ +We could use it on every case above, yes. But that would prevent us from obvious fixes and push the technical debt deeper under the rug with a hole under our apartment. + +
+ + +## 6. Cover vendor `*TestCase` classes and data providers + +There are two more cases where the `#[AllowMockObjectsWithoutExpectations]` attribute is needed and makes sense. + +
+ +We use a 3rd party test case class, that defines its "any" expectations for a reason. They might be used, or not. Depends on how we write the test. + +```diff + use Symfony\Component\Form\Test\TypeTestCase; + ++#[AllowMockObjectsWithoutExpectations] + final class SomeTest extends TypeTestCase + { + // ... + } +``` + +
+ +The next is a test method that uses a data provider. The data provider usually tests edge case values that may or may not trigger a method call: + +```diff + use PHPUnit\Framework\TestCase; + ++#[AllowMockObjectsWithoutExpectations] + final class SomeTest extends TestCase + { + #[DataProvider('provideData')] + public function test($input) + { + $someClass = $this->createMock(SomeClass::class); + $someClass + // implicit $this->any() here + ->expects($this->atLeastOnce()) + ->method('someMethod') + ->willReturn(100); + + // ... + } + + public static function provideData(): iterable + { + // ... + } + } +``` + +
+ +## 7. Move from object mocking to real objects + +
+"The best mock is no mock at all" +
+ +Before we even started the PHPUnit upgrade, we first eliminated the obvious cases that don't need any mocking at all. + +We looked for plain objects, DTOs, value objects, entities and documents and replaced them with real, natively typed objects. + +
+ +It can be as simple as using a simple `Request` directly: + +```diff + use PHPUnit\Framework\TestCase; + use Symfony\Component\HttpFoundation\Request; + use Symfony\Component\HttpFoundation\RequestStack; + + final class SomeTest extends TestCase + { + public function test() + { +- $request = $this->createMock(Request::class); ++ $request = new Request(); + +- $requestStack = $this->createMock(RequestStack::class); +- $requestStack->expects($this->atLeastOnce()) +- ->method('getMainRequest') +- ->willReturn($request); ++ $requestStack = new RequestStack($request); + + $this->someMethod($requestStack); + } +} +``` + +
+ +Simple as that. Same applies for entity/document objects. Instead of hard-to-read getter mocks, use real objects with real values and types: + +```diff +-$user = $this->createMock(User::class); ++$user = new User(); + +-$user->expects($this->any()) +- ->method('getName') +- ->willReturn('Tomas'); ++$user->setName('Tomas'); + +-$user->expects($this->any()) +- ->method('getAge') +- ->willReturn($age); ++$user->setAge($age); + + $service->process($user); +``` + +
+ +You can get the entity/document PHPStan spotter rule from `symplify/phpstan-rules` [here](https://github.com/symplify/phpstan-rules/blob/4b7aa41072850f9875b45272d263be3f4a183f40/src/Rules/Doctrine/NoDocumentMockingRule.php#L21). + +Also, give a go to experimental [Rector rule](https://github.com/rectorphp/rector-phpunit/pull/629) that manages to change these mocks to entities. It is a real time saver. + + +
+ +## Enjoy the Automated Upgrade + +We automated most of this work above, so you can let your agent handle the rest of the edge-cases. To get there, first enable the `phpunitCodeQuality` prepared set in your `rector.php`: + +```php +use Rector\Config\RectorConfig; + +return RectorConfig::configure() + ->withPreparedSets(phpunitCodeQuality: true) +``` + +And run Rector: + +```bash +vendor/bin/rector +``` + +
+ +Only then upgrade to PHPUnit 12.5 and run Rector with composer based set: + +```php +use Rector\Config\RectorConfig; + +return RectorConfig::configure() + ->withComposerBased(phpunit: true) +``` + +And run Rector again: + +```bash +vendor/bin/rector +``` + +It will automatically pick up the PHPUnit version and apply [the 12.5 set](https://github.com/rectorphp/rector-phpunit/blob/main/config/sets/phpunit120.php). + +
+ +That's all folks. I hope you enjoyed this manually written post. I certainly enjoyed writing it. + +As always, if you have improvement or bug report, head to Rector on Github and let us know. + +
+ +Happy coding! + + + diff --git a/tests/Controller/DemoControllerTest.php b/tests/Controller/DemoControllerTest.php index 357bd155d..067a6a5ff 100644 --- a/tests/Controller/DemoControllerTest.php +++ b/tests/Controller/DemoControllerTest.php @@ -16,11 +16,9 @@ final class DemoControllerTest extends AbstractTestCase { - #[Override] protected function setUp(): void { parent::setUp(); - $this->withoutMiddleware(ValidateCsrfToken::class); } diff --git a/tests/Documentation/DocumentationMenuFactoryTest.php b/tests/Documentation/DocumentationMenuFactoryTest.php index e9dd1d769..9ed69d2a5 100644 --- a/tests/Documentation/DocumentationMenuFactoryTest.php +++ b/tests/Documentation/DocumentationMenuFactoryTest.php @@ -17,7 +17,7 @@ final class DocumentationMenuFactoryTest extends TestCase protected function setUp(): void { $urlGenerator = $this->createMock(UrlGenerator::class); - $urlGenerator->expects($this->any()) + $urlGenerator ->method('action') ->willReturn('/index.html'); diff --git a/tests/FileSystem/RectorFinderTest.php b/tests/FileSystem/RectorFinderTest.php index 224816754..d529d88f6 100644 --- a/tests/FileSystem/RectorFinderTest.php +++ b/tests/FileSystem/RectorFinderTest.php @@ -31,9 +31,9 @@ public function testFindDuplicated(): void $uniqueShortNames = array_unique($shortNames); $uniqueLongNames = array_unique($longNames); - $this->assertSame( + $this->assertCount( count($uniqueShortNames), - count($uniqueLongNames), + $uniqueLongNames, 'There are no duplicated short class names.' ); } diff --git a/tests/GitHubMagicLink/BodyFactory/FixtureBodyFactory/FixtureBodyFactoryTest.php b/tests/GitHubMagicLink/BodyFactory/FixtureBodyFactory/FixtureBodyFactoryTest.php index a94ec6654..01ff3a859 100644 --- a/tests/GitHubMagicLink/BodyFactory/FixtureBodyFactory/FixtureBodyFactoryTest.php +++ b/tests/GitHubMagicLink/BodyFactory/FixtureBodyFactory/FixtureBodyFactoryTest.php @@ -13,11 +13,9 @@ final class FixtureBodyFactoryTest extends AbstractTestCase { private FixtureBodyFactory $fixtureBodyFactory; - #[Override] protected function setUp(): void { parent::setUp(); - $this->fixtureBodyFactory = $this->make(FixtureBodyFactory::class); } diff --git a/tests/GitHubMagicLink/BodyFactory/IssueBodyFactory/IssueBodyFactoryTest.php b/tests/GitHubMagicLink/BodyFactory/IssueBodyFactory/IssueBodyFactoryTest.php index 3f5c1e094..272878651 100644 --- a/tests/GitHubMagicLink/BodyFactory/IssueBodyFactory/IssueBodyFactoryTest.php +++ b/tests/GitHubMagicLink/BodyFactory/IssueBodyFactory/IssueBodyFactoryTest.php @@ -13,11 +13,9 @@ final class IssueBodyFactoryTest extends AbstractTestCase { private IssueBodyFactory $issueBodyFactory; - #[Override] protected function setUp(): void { parent::setUp(); - $this->issueBodyFactory = $this->make(IssueBodyFactory::class); } diff --git a/tests/GitHubMagicLink/BodyFactory/PullRequestDescriptionFactory/PullRequestDescriptionFactoryTest.php b/tests/GitHubMagicLink/BodyFactory/PullRequestDescriptionFactory/PullRequestDescriptionFactoryTest.php index e045ff9b6..dd7703bf4 100644 --- a/tests/GitHubMagicLink/BodyFactory/PullRequestDescriptionFactory/PullRequestDescriptionFactoryTest.php +++ b/tests/GitHubMagicLink/BodyFactory/PullRequestDescriptionFactory/PullRequestDescriptionFactoryTest.php @@ -13,11 +13,9 @@ final class PullRequestDescriptionFactoryTest extends AbstractTestCase { private PullRequestDescriptionFactory $pullRequestDescriptionFactory; - #[Override] protected function setUp(): void { parent::setUp(); - $this->pullRequestDescriptionFactory = $this->make(PullRequestDescriptionFactory::class); } diff --git a/tests/GitHubMagicLink/LinkFactory/FixtureLinkFactory/FixtureLinkFactoryTest.php b/tests/GitHubMagicLink/LinkFactory/FixtureLinkFactory/FixtureLinkFactoryTest.php index a8d8505e0..0fbf6bcc3 100644 --- a/tests/GitHubMagicLink/LinkFactory/FixtureLinkFactory/FixtureLinkFactoryTest.php +++ b/tests/GitHubMagicLink/LinkFactory/FixtureLinkFactory/FixtureLinkFactoryTest.php @@ -18,11 +18,9 @@ final class FixtureLinkFactoryTest extends AbstractTestCase { private FixtureLinkFactory $testFixtureLinkFactory; - #[Override] protected function setUp(): void { parent::setUp(); - $this->testFixtureLinkFactory = $this->make(FixtureLinkFactory::class); }