|
8 | 8 | namespace Magento\Framework\TestFramework\Unit\Helper; |
9 | 9 |
|
10 | 10 | use PHPUnit\Framework\MockObject\MockObject; |
| 11 | +use PHPUnit\Framework\MockObject\Rule\InvocationOrder; |
11 | 12 | use ReflectionClass; |
12 | 13 |
|
13 | 14 | /** |
@@ -42,4 +43,90 @@ protected function createPartialMockWithReflection(string $className, array $met |
42 | 43 | $mockBuilder->disableOriginalConstructor(); |
43 | 44 | return $mockBuilder->getMock(); |
44 | 45 | } |
| 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 | + } |
45 | 132 | } |
0 commit comments