• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

orchestral / testbench-core / 7147060874

08 Dec 2023 10:45PM UTC coverage: 92.508% (-0.2%) from 92.706%
7147060874

Pull #166

github

crynobone
wip

Signed-off-by: Mior Muhammad Zaki <crynobone@gmail.com>
Pull Request #166: PestPHP Improvements

76 of 83 new or added lines in 7 files covered. (91.57%)

2 existing lines in 2 files now uncovered.

1136 of 1228 relevant lines covered (92.51%)

45.28 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.23
/src/Concerns/InteractsWithPHPUnit.php
1
<?php
2

3
namespace Orchestra\Testbench\Concerns;
4

5
use Closure;
6
use Illuminate\Support\Collection;
7
use Orchestra\Testbench\Contracts\Attributes\Resolvable as ResolvableContract;
8
use Orchestra\Testbench\PHPUnit\AttributeParser;
9
use PHPUnit\Framework\TestCase as PHPUnitTestCase;
10
use PHPUnit\Metadata\Annotation\Parser\Registry as PHPUnit10Registry;
11
use PHPUnit\Util\Annotation\Registry as PHPUnit9Registry;
12
use ReflectionClass;
13

14
use function Orchestra\Testbench\phpunit_version_compare;
15

16
/**
17
 * @internal
18
 *
19
 * @phpstan-import-type TTestingFeature from \Orchestra\Testbench\PHPUnit\AttributeParser
20
 * @phpstan-import-type TAttributes from \Orchestra\Testbench\PHPUnit\AttributeParser
21
 */
22
trait InteractsWithPHPUnit
23
{
24
    /**
25
     * The cached test case setUp resolver.
26
     *
27
     * @var (\Closure(\Closure):(void))|null
28
     */
29
    protected $testCaseSetUpCallback;
30

31
    /**
32
     * The cached test case tearDown resolver.
33
     *
34
     * @var (\Closure(\Closure):(void))|null
35
     */
36
    protected $testCaseTearDownCallback;
37

38
    /**
39
     * The cached uses for test case.
40
     *
41
     * @var array<class-string, class-string>|null
42
     */
43
    protected static $cachedTestCaseUses;
44

45
    /**
46
     * The cached class attributes for test case.
47
     *
48
     * @var array<string, array<int, array{key: class-string, instance: object}>>
49
     *
50
     * @phpstan-var array<string, array<int, array{key: class-string<TTestingFeature>, instance: TTestingFeature}>>
51
     */
52
    protected static $cachedTestCaseClassAttributes = [];
53

54
    /**
55
     * The cached method attributes for test case.
56
     *
57
     * @var array<string, array<int, array{key: class-string, instance: object}>>
58
     *
59
     * @phpstan-var array<string, array<int, array{key: class-string<TTestingFeature>, instance: TTestingFeature}>>
60
     */
61
    protected static $cachedTestCaseMethodAttributes = [];
62

63
    /**
64
     * The method attributes for test case.
65
     *
66
     * @var array<string, array<int, array{key: class-string, instance: object}>>
67
     *
68
     * @phpstan-var array<string, array<int, array{key: class-string<TTestingFeature>, instance: TTestingFeature}>>
69
     */
70
    protected static $testCaseMethodAttributes = [];
71

72
    /**
73
     * Determine if the trait is used within testing.
74
     *
75
     * @return bool
76
     */
77
    public function isRunningTestCase(): bool
78
    {
79
        return $this instanceof PHPUnitTestCase || static::usesTestingConcern();
119✔
80
    }
81

82
    /**
83
     * Uses testing feature (attribute) on the current test.
84
     *
85
     * @param  object  $attribute
86
     * @return void
87
     *
88
     * @phpstan-param TAttributes $attribute
89
     */
90
    public function usesTestingFeature($attribute): void
91
    {
92
        $instance = new ReflectionClass($this);
4✔
93

94
        if (
95
            ! $this instanceof PHPUnitTestCase
4✔
96
            || ! AttributeParser::validAttribute($attribute)
4✔
97
            || $instance->isAnonymous()
4✔
98
        ) {
99
            return;
×
100
        }
101

102
        $attribute = $attribute instanceof ResolvableContract ? $attribute->resolve() : $attribute;
4✔
103

104
        if (\is_null($attribute)) {
4✔
105
            return;
×
106
        }
107

108
        $className = $instance->getName();
4✔
109
        $methodName = phpunit_version_compare('10', '>=')
4✔
110
            ? $this->name() /** @phpstan-ignore-line */
4✔
111
            : $this->getName(false); /** @phpstan-ignore-line */
×
112
        if (! isset(static::$testCaseMethodAttributes["{$className}:{$methodName}"])) {
4✔
113
            static::$testCaseMethodAttributes["{$className}:{$methodName}"] = [];
4✔
114
        }
115

116
        /** @var class-string<TTestingFeature> $name */
117
        $name = \get_class($attribute);
4✔
118

119
        array_push(static::$testCaseMethodAttributes["{$className}:{$methodName}"], [
4✔
120
            'key' => $name,
4✔
121
            'instance' => $attribute,
4✔
122
        ]);
4✔
123
    }
124

125
    /**
126
     * Resolve PHPUnit method annotations.
127
     *
128
     * @phpunit-overrides
129
     *
130
     * @return \Illuminate\Support\Collection<string, mixed>
131
     */
132
    protected function resolvePhpUnitAnnotations(): Collection
133
    {
134
        $instance = new ReflectionClass($this);
114✔
135

136
        if (! $this instanceof PHPUnitTestCase || $instance->isAnonymous()) {
114✔
137
            return new Collection();
1✔
138
        }
139

140
        [$registry, $methodName] = phpunit_version_compare('10', '>=')
113✔
141
            ? [PHPUnit10Registry::getInstance(), $this->name()] /** @phpstan-ignore-line */
113✔
142
            : [PHPUnit9Registry::getInstance(), $this->getName(false)]; /** @phpstan-ignore-line */
×
143

144
        /** @var array<string, mixed> $annotations */
145
        $annotations = rescue(
113✔
146
            fn () => $registry->forMethod($instance->getName(), $methodName)->symbolAnnotations(),
113✔
147
            [],
113✔
148
            false
113✔
149
        );
113✔
150

151
        return Collection::make($annotations);
113✔
152
    }
153

154
    /**
155
     * Resolve PHPUnit method attributes.
156
     *
157
     * @phpunit-overrides
158
     *
159
     * @return \Illuminate\Support\Collection<class-string, array<int, object>>
160
     *
161
     * @phpstan-return \Illuminate\Support\Collection<class-string<TTestingFeature>, array<int, TTestingFeature>>
162
     */
163
    protected function resolvePhpUnitAttributes(): Collection
164
    {
165
        $instance = new ReflectionClass($this);
114✔
166

167
        if (! $this instanceof PHPUnitTestCase || $instance->isAnonymous()) {
114✔
168
            return new Collection();
1✔
169
        }
170

171
        $className = $instance->getName();
113✔
172
        $methodName = phpunit_version_compare('10', '>=')
113✔
173
            ? $this->name() /** @phpstan-ignore-line */
113✔
174
            : $this->getName(false); /** @phpstan-ignore-line */
×
175
        if (! isset(static::$cachedTestCaseClassAttributes[$className])) {
113✔
176
            static::$cachedTestCaseClassAttributes[$className] = rescue(static function () use ($className) {
53✔
177
                return AttributeParser::forClass($className);
53✔
178
            }, [], false);
53✔
179
        }
180

181
        if (! isset(static::$cachedTestCaseMethodAttributes["{$className}:{$methodName}"])) {
113✔
182
            static::$cachedTestCaseMethodAttributes["{$className}:{$methodName}"] = rescue(static function () use ($className, $methodName) {
112✔
183
                return AttributeParser::forMethod($className, $methodName);
112✔
184
            }, [], false);
112✔
185
        }
186

187
        /** @var \Illuminate\Support\Collection<class-string<TTestingFeature>, array<int, TTestingFeature>> $attributes */
188
        $attributes = Collection::make(array_merge(
113✔
189
            static::$cachedTestCaseClassAttributes[$className],
113✔
190
            static::$cachedTestCaseMethodAttributes["{$className}:{$methodName}"],
113✔
191
            static::$testCaseMethodAttributes["{$className}:{$methodName}"] ?? [],
113✔
192
        ))->groupBy('key')
113✔
193
            ->map(static function ($attributes) {
113✔
194
                /** @var \Illuminate\Support\Collection<int, array{key: class-string<TTestingFeature>, instance: TTestingFeature}> $attributes */
195
                return $attributes->map(static function ($attribute) {
11✔
196
                    /** @var array{key: class-string<TTestingFeature>, instance: TTestingFeature} $attribute */
197
                    return $attribute['instance'];
11✔
198
                });
11✔
199
            });
113✔
200

201
        return $attributes;
113✔
202
    }
203

204
    /**
205
     * Determine if the trait is used Orchestra\Testbench\Concerns\Testing trait.
206
     *
207
     * @param  class-string|null  $trait
208
     * @return bool
209
     */
210
    public static function usesTestingConcern(?string $trait = null): bool
211
    {
212
        return isset(static::cachedUsesForTestCase()[$trait ?? Testing::class]);
119✔
213
    }
214

215
    /**
216
     * Define or get the cached uses for test case.
217
     *
218
     * @return array<class-string, class-string>
219
     */
220
    public static function cachedUsesForTestCase(): array
221
    {
222
        if (\is_null(static::$cachedTestCaseUses)) {
119✔
223
            /** @var array<class-string, class-string> $uses */
224
            $uses = array_flip(class_uses_recursive(static::class));
3✔
225

226
            static::$cachedTestCaseUses = $uses;
3✔
227
        }
228

229
        return static::$cachedTestCaseUses;
119✔
230
    }
231

232
    /**
233
     * Prepare the testing environment before the running the test case.
234
     *
235
     * @return void
236
     *
237
     * @codeCoverageIgnore
238
     */
239
    public static function setUpBeforeClassUsingPHPUnit(): void
240
    {
241
        static::cachedUsesForTestCase();
242
    }
243

244
    /**
245
     * Define the setUp environment using callback.
246
     *
247
     * @param  \Closure(\Closure):void  $setUp
248
     * @return void
249
     */
250
    public function setUpTheEnvironmentUsing(Closure $setUp): void
251
    {
NEW
252
        $this->testCaseSetUpCallback = $setUp;
×
253
    }
254

255
    /**
256
     * Define the tearDown environment using callback.
257
     *
258
     * @param  \Closure(\Closure):void  $tearDown
259
     * @return void
260
     */
261
    public function tearDownTheEnvironmentUsing(Closure $tearDown): void
262
    {
NEW
263
        $this->testCaseTearDownCallback = $tearDown;
×
264
    }
265

266
    /**
267
     * Clean up the testing environment before the next test case.
268
     *
269
     * @return void
270
     *
271
     * @codeCoverageIgnore
272
     */
273
    public static function tearDownAfterClassUsingPHPUnit(): void
274
    {
275
        static::$cachedTestCaseUses = null;
276
        static::$cachedTestCaseClassAttributes = [];
277
        static::$cachedTestCaseMethodAttributes = [];
278

279
        $registry = phpunit_version_compare('10', '>=')
280
            ? PHPUnit10Registry::getInstance() /** @phpstan-ignore-line */
281
            : PHPUnit9Registry::getInstance(); /** @phpstan-ignore-line */
282
        (function () {
283
            $this->classDocBlocks = [];
284
            $this->methodDocBlocks = [];
285
        })->call($registry);
286
    }
287
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc