You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: resources/blog/posts/2026/2026-02-07-upgrade-to-phpunit-125-in-7-diffs.md
+35-35Lines changed: 35 additions & 35 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,17 +1,17 @@
1
1
---
2
2
id: 83
3
-
title: "Upgrade to PHPUnit 12.5 in 7 diffs"
3
+
title: "Upgrade to PHPUnit 12.5 in 7 Diffs"
4
4
perex: |
5
-
PHPUnit 12 was released a year ago, but only PHPUnit 12.5 released in December 2025 includes valuable features that are worth the once a year ugprade.
5
+
PHPUnit 12 was released a year ago, but only PHPUnit 12.5 released in December 2025 includes valuable features that are worth it.
6
6
7
-
The most important change, that will affect your code, is that mocks are now much more strict. And there also stubs... a mock that does not nothing. How to spot them and separate them?
7
+
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?
8
8
9
-
Are you curious how to get from 4000 notices to under 100 in 7 diffs? Read on.
9
+
Curious how to get from 4000 notices to under 100 in 7 diffs? Read on.
10
10
---
11
11
12
-
What is difference between a mock and a stub? You didn't have to care untill PHPUnit 12.5, but now you do.
12
+
What is the difference between a mock and a stub? You did not have to care until PHPUnit 12.5, but now you do.
13
13
14
-
Why? Because PHPUnit now complains their miss-use verboselly. And there is no way to ignore it:
14
+
Why? Because PHPUnit now complains about their misuse very verbosely. There is no way to ignore it:
This leads us to the first and simplest change we can make.
64
64
65
65
<br>
66
66
67
-
## 1. Use `createStub()`over `createMock()` in args
67
+
## 1. Use `createStub()`instead of `createMock()` in arguments
68
68
69
-
The first one are simple as:
69
+
The first cases are as simple as:
70
70
71
71
```diff
72
72
$someClass = new SomeClass(
@@ -86,7 +86,7 @@ Also variable assigns:
86
86
$someClass = new SomeClass($someDependency);
87
87
```
88
88
89
-
Or coalesce as argument:
89
+
Or coalesce directly in the argument:
90
90
91
91
```diff
92
92
-$someClass = new SomeClass(
@@ -117,9 +117,9 @@ But also property fetches without any expectations:
117
117
118
118
## 2. Inline once-used Mocks Property to a Variable
119
119
120
-
This is not change in PHPUnit 12.5 per se, bit it helps with changes that come with it. During the upgrade, I've noticed some properties are used just once.
120
+
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.
121
121
122
-
Properties are not variables for one reason: to be used across multiple methods. So lets fix that:
122
+
Properties are not variables for one reason: to be used across multiple methods. Let us fix that:
123
123
124
124
```diff
125
125
-private MockObject $someDependency;
@@ -143,7 +143,7 @@ We have less code to read for us and GPT, and also can move to `createStub()` wi
143
143
144
144
<br>
145
145
146
-
## 3. Remove never used isolated mocks, as well as dead
146
+
## 3. Remove never used isolated mocks and dead code
147
147
148
148
Speaking of dead code, the mocks to stub narrowign also surfaces another issue: never used mocks that live on their own island.
What is wrong with this code snippet, apart 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.
161
+
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.
162
162
163
-
It's a dead code. Remove it:
163
+
It is dead code. Remove it:
164
164
165
165
```diff
166
166
-$this->createMock(SomeClass::class)
@@ -171,7 +171,7 @@ It's a dead code. Remove it:
171
171
172
172
<br>
173
173
174
-
Beware, this can be a as complex as well defined and typed property... that is never used. Dead code, remove it:
174
+
Beware, this can be as complex as a well defined and typed property... that is never used. Dead code, remove it:
175
175
176
176
```diff
177
177
-private MockObject $mockProperty;
@@ -189,7 +189,7 @@ Beware, this can be a as complex as well defined and typed property... that is n
189
189
190
190
## 4. From `$this->any()` to explicit expectations
191
191
192
-
PHPUnit now also deprecated used of `$this->any()` expectations. Wise choice, as its says "we expect 0, or 1 or any number of occurances". This code as well could be removed.
192
+
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.
193
193
194
194
<br>
195
195
@@ -208,7 +208,7 @@ $someClass
208
208
->willReturn(100);
209
209
```
210
210
211
-
Both will be most reported by PHPUnit as stubs. They have 0 expectations (amongs other numbers). So how do we fix that? Change we used before will not be enought (nor working):
211
+
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:
@@ -220,7 +220,7 @@ Both will be most reported by PHPUnit as stubs. They have 0 expectations (amongs
220
220
We have to be honest here, and it might require to understand the code.
221
221
222
222
* Is it a dummy method defined in `setUp()` method, in case it will be called any further in the codebase?
223
-
* Is it implicit `$this->any()`, just becaused we forgot to add explicit number?
223
+
* Is it implicit `$this->any()`, just because we forgot to add explicit number?
224
224
225
225
<br>
226
226
@@ -238,7 +238,7 @@ But what about the `setUp()` method? Do we have to now go through all the code a
238
238
<br>
239
239
240
240
241
-
## 5. Add `#[AllowMockObjectsWithoutExpectations]` for setUp() optionals
241
+
## 5. Add `#[AllowMockObjectsWithoutExpectations]` for optional setUp mocks
242
242
243
243
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:
244
244
@@ -267,8 +267,8 @@ public function testNotUsing()
267
267
}
268
268
```
269
269
270
-
Here we have one mocked object as a property with *any* expectations. Then 2 test methods. The 1st one is using mock as a mock.
271
-
The 2nd test method is not, so it's a stub from its point of view.
270
+
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.
271
+
The second test method does not, so from its point of view it is a stub.
272
272
273
273
(Also, another method can be using the property, but never calling the mocked method, so it's a stub as well).
274
274
@@ -293,14 +293,14 @@ This attribute will silence the notices about stubs in this test class.
293
293
294
294
<br>
295
295
296
-
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 whole under our apparment.
296
+
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.
297
297
298
298
<br>
299
299
300
300
301
-
## 6. Cover Vendor`*TestCase` and Data Providers
301
+
## 6. Cover vendor`*TestCase`classes and data providers
302
302
303
-
There are 2 more case where the `#[AllowMockObjectsWithoutExpectations]` attribute is needed and makes sense.
303
+
There are two more cases where the `#[AllowMockObjectsWithoutExpectations]` attribute is needed and makes sense.
304
304
305
305
<br>
306
306
@@ -318,7 +318,7 @@ We use a 3rd party test case class, that defines its "any" expectations for a re
318
318
319
319
<br>
320
320
321
-
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:
321
+
The next is a test method that uses a data provider. The data provider usually tests edgecase values that may or may not trigger a method call:
322
322
323
323
```diff
324
324
use PHPUnit\Framework\TestCase;
@@ -348,15 +348,15 @@ The next is a test method that uses a data provider. The data provider usually t
348
348
349
349
<br>
350
350
351
-
## 7. Move from Object mocking to... Objects
351
+
## 7. Move from object mocking to real objects
352
352
353
353
<blockquoteclass="blockquote">
354
354
"The best mock is no mock at all"
355
355
</blockquote>
356
356
357
-
Before we event dived into PHPUnit upgrade, we first eliminated the obvious cases that don't need any mocking at all.
357
+
Before we even started the PHPUnit upgrade, we first eliminated the obvious cases that don't need any mocking at all.
358
358
359
-
We looked for plain objects, DTOs, value objects, entities and documents and replacted them will 100 % real, natively typed objects.
359
+
We looked for plain objects, DTOs, value objects, entities and documents and replaced them with real, natively typed objects.
360
360
361
361
<br>
362
362
@@ -387,7 +387,7 @@ It can be as simple as using a simple `Request` directly:
387
387
388
388
<br>
389
389
390
-
Simple as that. Same applies for entity/document objects. Instead of hard-to-read gettter mocks, use real objects with real values and types:
390
+
Simple as that. Same applies for entity/document objects. Instead of hard-to-read getter mocks, use real objects with real values and types:
391
391
392
392
```diff
393
393
-$user = $this->createMock(User::class);
@@ -410,12 +410,12 @@ Simple as that. Same applies for entity/document objects. Instead of hard-to-rea
410
410
411
411
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).
412
412
413
-
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's a real time-saver.
413
+
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 timesaver.
414
414
415
415
416
416
<br>
417
417
418
-
## Enjoy Automated Upgrade
418
+
## Enjoy the Automated Upgrade
419
419
420
420
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`:
421
421
@@ -453,7 +453,7 @@ It will automatically pick up the PHPUnit version and apply [the 12.5 set](https
453
453
454
454
<br>
455
455
456
-
That's all folks. Hope you've enjoyed this manually-written post. I surelly did enjoy writing it.
456
+
That's all folks. I hope you enjoyed this manuallywritten post. I certainly enjoyed writing it.
457
457
458
458
As always, if you have improvement or bug report, head to Rector on Github and let us know.
0 commit comments