Skip to content

Commit 2e38df7

Browse files
AC-15498: PHPUnit 12 Upgrade | Added createInvocationMatcher() to MockCreationTrait
1 parent aa50831 commit 2e38df7

File tree

2 files changed

+98
-20
lines changed

2 files changed

+98
-20
lines changed

app/code/Magento/Reports/Test/Unit/Model/ResourceModel/Order/CollectionTest.php

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,19 @@
2525
use Magento\Store\Model\ScopeInterface;
2626
use Magento\Store\Model\Store;
2727
use Magento\Store\Model\StoreManagerInterface;
28-
use PHPUnit\Framework\Attributes\DataProvider;
2928
use PHPUnit\Framework\MockObject\Matcher\InvokedCount;
3029
use PHPUnit\Framework\MockObject\MockObject;
3130
use PHPUnit\Framework\TestCase;
3231
use Psr\Log\LoggerInterface;
33-
32+
use PHPUnit\Framework\Attributes\DataProvider;
33+
use Magento\Framework\TestFramework\Unit\Helper\MockCreationTrait;
3434
/**
3535
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
3636
*/
3737
class CollectionTest extends TestCase
3838
{
39+
use MockCreationTrait;
40+
3941
/**
4042
* @var Collection
4143
*/
@@ -229,25 +231,19 @@ public function testCheckIsLive(): void
229231
* @param int $useAggregatedData
230232
* @param string $mainTable
231233
* @param int $isFilter
232-
* @param InvokedCount $getIfNullSqlResult
234+
* @param string $getIfNullSqlResult
233235
*
234236
* @return void
235237
*/
236238
#[DataProvider('useAggregatedDataDataProvider')]
237239
public function testPrepareSummary($useAggregatedData, $mainTable, $isFilter, $getIfNullSqlResult): void
238240
{
239-
// Convert string matcher to actual matcher
240-
if ($getIfNullSqlResult === 'never') {
241-
$getIfNullSqlResult = $this->never();
242-
} elseif (str_starts_with($getIfNullSqlResult, 'exactly_')) {
243-
$count = (int)str_replace('exactly_', '', $getIfNullSqlResult);
244-
$getIfNullSqlResult = $this->exactly($count);
245-
}
246-
247241
$range = '';
248242
$customStart = 1;
249243
$customEnd = 10;
250244

245+
$getIfNullSqlResult = $this->createInvocationMatcher($getIfNullSqlResult);
246+
251247
$this->scopeConfigMock
252248
->expects($this->once())
253249
->method('getValue')
@@ -315,6 +311,7 @@ public function testGetDateRangeFirstPart($range, $customStart, $customEnd, $exp
315311
#[DataProvider('secondPartDateRangeDataProvider')]
316312
public function testGetDateRangeSecondPart($range, $customStart, $customEnd, $config, $expectedYear): void
317313
{
314+
318315
$this->scopeConfigMock
319316
->expects($this->once())
320317
->method('getValue')
@@ -355,21 +352,15 @@ public function testAddItemCountExpr(): void
355352
* @param int $isFilter
356353
* @param int $useAggregatedData
357354
* @param string $mainTable
358-
* @param InvokedCount $getIfNullSqlResult
355+
* @param string $getIfNullSqlResult
359356
*
360357
* @return void
361358
*/
362359
#[DataProvider('totalsDataProvider')]
363360
public function testCalculateTotals($isFilter, $useAggregatedData, $mainTable, $getIfNullSqlResult): void
364361
{
365-
// Convert string matcher to actual matcher
366-
if ($getIfNullSqlResult === 'never') {
367-
$getIfNullSqlResult = $this->never();
368-
} elseif (str_starts_with($getIfNullSqlResult, 'exactly_')) {
369-
$count = (int)str_replace('exactly_', '', $getIfNullSqlResult);
370-
$getIfNullSqlResult = $this->exactly($count);
371-
}
372-
362+
$getIfNullSqlResult = $this->createInvocationMatcher($getIfNullSqlResult);
363+
373364
$this->scopeConfigMock
374365
->expects($this->once())
375366
->method('getValue')

lib/internal/Magento/Framework/TestFramework/Unit/Helper/MockCreationTrait.php

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
namespace Magento\Framework\TestFramework\Unit\Helper;
99

1010
use PHPUnit\Framework\MockObject\MockObject;
11+
use PHPUnit\Framework\MockObject\Rule\InvocationOrder;
1112
use ReflectionClass;
1213

1314
/**
@@ -42,4 +43,90 @@ protected function createPartialMockWithReflection(string $className, array $met
4243
$mockBuilder->disableOriginalConstructor();
4344
return $mockBuilder->getMock();
4445
}
46+
47+
/**
48+
* Convert a string matcher specification to a PHPUnit invocation matcher.
49+
*
50+
* This is useful in data providers where you cannot call PHPUnit matcher methods
51+
* directly (since they are non-static). Instead, pass a string representation
52+
* and convert it to the actual matcher in your test method.
53+
*
54+
* Supported string formats:
55+
* - 'never' → $this->never()
56+
* - 'once' → $this->once()
57+
* - 'any' → $this->any()
58+
* - 'atLeastOnce' → $this->atLeastOnce()
59+
* - 'exactly_N' → $this->exactly(N) e.g., 'exactly_3'
60+
* - 'atLeast_N' → $this->atLeast(N) e.g., 'atLeast_2'
61+
* - 'atMost_N' → $this->atMost(N) e.g., 'atMost_5'
62+
*
63+
* @param string $matcherSpec The string specification of the matcher
64+
* @return InvocationOrder The PHPUnit invocation matcher
65+
* @throws \InvalidArgumentException If the matcher specification is not recognized
66+
*
67+
* @example
68+
* // In data provider:
69+
* public static function dataProvider(): array
70+
* {
71+
* return [
72+
* ['methodName', 'returnValue', 'once'],
73+
* ['methodName', 'returnValue', 'exactly_3'],
74+
* ['methodName', 'returnValue', 'never'],
75+
* ];
76+
* }
77+
*
78+
* // In test method:
79+
* public function testMethod(string $method, string $returnValue, string $matcherSpec): void
80+
* {
81+
* $matcher = $this->createInvocationMatcher($matcherSpec);
82+
* $mock->expects($matcher)->method($method)->willReturn($returnValue);
83+
* }
84+
*/
85+
protected function createInvocationMatcher(string $matcherSpec): InvocationOrder
86+
{
87+
// Handle simple string matchers
88+
return match ($matcherSpec) {
89+
'never' => $this->never(),
90+
'once' => $this->once(),
91+
'any' => $this->any(),
92+
'atLeastOnce' => $this->atLeastOnce(),
93+
default => $this->parseParameterizedMatcher($matcherSpec),
94+
};
95+
}
96+
97+
/**
98+
* Parse parameterized matcher specifications like 'exactly_3', 'atLeast_2', 'atMost_5'.
99+
*
100+
* @param string $matcherSpec The parameterized matcher specification
101+
* @return InvocationOrder
102+
* @throws \InvalidArgumentException If the matcher specification is not recognized
103+
*/
104+
private function parseParameterizedMatcher(string $matcherSpec): InvocationOrder
105+
{
106+
// Handle 'exactly_N' format
107+
if (str_starts_with($matcherSpec, 'exactly_')) {
108+
$count = (int)substr($matcherSpec, 8);
109+
return $this->exactly($count);
110+
}
111+
112+
// Handle 'atLeast_N' format
113+
if (str_starts_with($matcherSpec, 'atLeast_')) {
114+
$count = (int)substr($matcherSpec, 8);
115+
return $this->atLeast($count);
116+
}
117+
118+
// Handle 'atMost_N' format
119+
if (str_starts_with($matcherSpec, 'atMost_')) {
120+
$count = (int)substr($matcherSpec, 7);
121+
return $this->atMost($count);
122+
}
123+
124+
throw new \InvalidArgumentException(
125+
sprintf(
126+
'Unrecognized matcher specification: "%s". ' .
127+
'Supported: never, once, any, atLeastOnce, exactly_N, atLeast_N, atMost_N',
128+
$matcherSpec
129+
)
130+
);
131+
}
45132
}

0 commit comments

Comments
 (0)