-
-
Notifications
You must be signed in to change notification settings - Fork 20
feat(skip): add #[Skip] attribute to skip tests declaratively #314
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
48 commits
Select commit
Hold shift + click to select a range
be1fb8d
feat(test): add #[Skip] attribute to skip tests declaratively
Meacue 79109b6
test(test): cover the #[Skip] v1 semantics with unit and feature tests
Meacue dd86e2c
docs(skills): document #[Skip] in write-tests, plugin-author and flakβ¦
Meacue e0e2858
build: prepare root constraints for the testo/test 0.2.0 release
Meacue 708787c
fix(test): mark #[Skip] as Interceptable with SkipInterceptor as fallβ¦
Meacue 62a12da
refactor(test): declare SkipInterceptor conflict policy explicitly
Meacue e766061
docs(test): correct the fallback coverage claim in the Skip docblock
Meacue 29eb900
docs(test): explain why SkipInterceptor takes no Skip constructor parβ¦
Meacue 93f8580
docs(test): document that #[Skip] is inert on bench and inline cases
Meacue 9f44b4a
fix(test): carry the test description into the synthetic Skipped result
Meacue 9cc7603
test(test): cover class-level #[Skip] standalone fallback without Tesβ¦
Meacue 83a8237
test(test): pin single delivery of class-level #[Skip] with both pathβ¦
Meacue ce84744
test(lifecycle): pin class-level hooks for fully parked cases end-to-end
Meacue 9c98bab
test(test): drop a cannot-fail assertion and pin the enabled functionβ¦
Meacue 806ab59
fix(test): load the function stub in SkipFeatureTest regardless of ruβ¦
Meacue 9f4f75c
docs(test): fix #[Skip] docs and strengthen its tests
Meacue 26dd926
docs(test): clarify skip reporter visibility
Meacue 7764b46
fix(test): deactivate #[Skip] tests instead of undefining them
Meacue 19b6580
style(test): order static methods first in the #[Skip] sources
Meacue 6182310
docs(test): say what each #[Skip] stub proves and tighten the attribuβ¦
Meacue 967e2c6
test(test): pin the #[Skip] fallback message and align the stubs withβ¦
Meacue fca9911
docs(skills): describe #[Skip] deactivation and its package in the skβ¦
Meacue 153e46f
test(test): use the typed Assert facade in the #[Skip] tests
Meacue 9324f84
test(test): pin that an overriding method inherits the #[Skip] of itsβ¦
Meacue d755af1
test(test): add positive controls for the #[Retry]/#[Repeat] skip checks
Meacue 9d504d6
refactor(test): say "skipped" instead of "parked" across the #[Skip] β¦
Meacue 2f16f36
style(test): use a # line comment in SkipWithHooksStub
Meacue 0bcca01
test(test): pin what the #[Skip] docblocks promise
Meacue aa048ea
docs(test): say "directory" and "case" instead of "catalog" in the #[β¦
Meacue 4a454e8
build: revert the hand-edited testo/test root constraints
Meacue 58bd873
test(test): make the #[Retry] control in SkipWithRetryStub history-proof
Meacue 2c16e71
Merge branch '1.x' into feat/skip-attribute
roxblnfk 2d95649
feat(core): add `CaseInterceptable` for test-level attributes on the β¦
roxblnfk 6421baa
feat(skip): move `#[Skip]` into its own `testo/skip` plugin
roxblnfk ff59c73
docs(skip): drop the `TestPlugin` wiring and the issue reference fromβ¦
roxblnfk b9fd5d6
feat(skip): wire `#[Skip]` through `CaseInterceptable` instead of `Teβ¦
roxblnfk 0dbef9a
Merge remote-tracking branch 'origin/1.x' into fork/Meacue/feat/skip-β¦
roxblnfk e9d1043
build(skip): align the `testo/skip` version constraints with the plugβ¦
roxblnfk 30b6692
fix(skip): require the core version the monorepo actually resolves
roxblnfk 44159a6
docs(core): say what the test-attribute scan constrains instead of reβ¦
roxblnfk 58753e4
feat(core): flag a test as skipped on its definition
roxblnfk 3a9f178
feat(lifecycle): run no hook for a skipped test or a case without a tβ¦
roxblnfk b897d6b
feat(skip): flag `#[Skip]` tests at location and report them from theβ¦
roxblnfk 22c1c2b
Merge branch '1.x' into feat/skip-attribute
roxblnfk 3db685e
docs(skip): say what the `#[Skip]` interceptors constrain instead of β¦
roxblnfk 3535db2
fix(skip): resolve the skip reason by reflection instead of by the suβ¦
roxblnfk caf9e90
docs(lifecycle): say what hook discovery ignores instead of promisingβ¦
roxblnfk c967a3d
test(skip): pin the empty-reason message for a function
roxblnfk File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
73 changes: 73 additions & 0 deletions
73
plugin/lifecycle/tests/Feature/FullySkippedCaseFeatureTest.php
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Tests\Lifecycle\Feature; | ||
|
|
||
| use Testo\Assert; | ||
| use Testo\Codecov\Covers; | ||
| use Testo\Core\Value\Status; | ||
| use Testo\Lifecycle\Internal\LifecycleInterceptor; | ||
| use Testo\Test; | ||
| use Testo\Testing\Attribute\TestingSuite; | ||
| use Testo\Testing\Helper\TestRunner; | ||
| use Tests\Lifecycle\Stub\FullySkipped\FullySkippedClassStub; | ||
| use Tests\Lifecycle\Stub\FullySkipped\FullySkippedFunctionState; | ||
|
|
||
| /** | ||
| * End-to-end regression test for {@see LifecycleInterceptor}: a case whose every test is flagged | ||
| * skipped ahead of the run β here by `#[Skip]` from `testo/skip` β has nothing to set up, so none | ||
| * of its hooks fire, `#[BeforeClass]`/`#[AfterClass]` included. | ||
| * | ||
| * The skipped tests stay active (the case is still located, run and reported), which is why the | ||
| * interceptor has to look at the skipped flag and not only at the active test set. Both case | ||
| * shapes are pinned here through the real pipeline: the methods of a class-based case and the | ||
| * free functions of a function-based one. | ||
| */ | ||
| #[Test] | ||
| #[Covers(LifecycleInterceptor::class)] | ||
| #[TestingSuite(path: __DIR__ . '/../Stub/FullySkipped')] | ||
| final class FullySkippedCaseFeatureTest | ||
| { | ||
| public function __construct() | ||
| { | ||
| # Functions are not autoloadable: load the stub so TestRunner::runTest() can resolve the | ||
| # function names below. The pipeline re-includes the same file (include_once) when it runs. | ||
| require_once __DIR__ . '/../Stub/FullySkipped/fully_skipped_functions.php'; | ||
| } | ||
|
|
||
| /** | ||
| * The function-based case shape: no hook of any kind fires, and the skipped tests are still | ||
| * reported. | ||
| */ | ||
| public function noHookRunsForAFullySkippedFunctionCase(): void | ||
| { | ||
| $beforeClass = FullySkippedFunctionState::$beforeClassCalls; | ||
| $afterClass = FullySkippedFunctionState::$afterClassCalls; | ||
| $beforeTest = FullySkippedFunctionState::$beforeTestCalls; | ||
| $afterTest = FullySkippedFunctionState::$afterTestCalls; | ||
|
|
||
| $result = TestRunner::runTest('Tests\Lifecycle\Stub\FullySkipped\skippedFnOne'); | ||
|
|
||
| Assert::same($result->status, Status::Skipped); | ||
| Assert::same(FullySkippedFunctionState::$beforeClassCalls - $beforeClass, 0); | ||
| Assert::same(FullySkippedFunctionState::$afterClassCalls - $afterClass, 0); | ||
| Assert::same(FullySkippedFunctionState::$beforeTestCalls - $beforeTest, 0); | ||
| Assert::same(FullySkippedFunctionState::$afterTestCalls - $afterTest, 0); | ||
| } | ||
|
|
||
| /** | ||
| * The class-based analog: the class-level hooks stay silent for a fully skipped class. | ||
| */ | ||
| public function noHookRunsForAFullySkippedClassCase(): void | ||
| { | ||
| $beforeClass = FullySkippedClassStub::$beforeClassCalls; | ||
| $afterClass = FullySkippedClassStub::$afterClassCalls; | ||
|
|
||
| $result = TestRunner::runTest([FullySkippedClassStub::class, 'skipped']); | ||
|
|
||
| Assert::same($result->status, Status::Skipped); | ||
| Assert::same(FullySkippedClassStub::$beforeClassCalls - $beforeClass, 0); | ||
| Assert::same(FullySkippedClassStub::$afterClassCalls - $afterClass, 0); | ||
| } | ||
| } |
42 changes: 42 additions & 0 deletions
42
plugin/lifecycle/tests/Stub/FullySkipped/FullySkippedClassStub.php
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Tests\Lifecycle\Stub\FullySkipped; | ||
|
|
||
| use Testo\Lifecycle\AfterClass; | ||
| use Testo\Lifecycle\BeforeClass; | ||
| use Testo\Test; | ||
| use Testo\Skip; | ||
|
|
||
| /** | ||
| * Class-based analog of the fully skipped function case in `fully_skipped_functions.php` | ||
| * ({@see skippedFnOne()}): a case with no test to run gets no class-level hooks β pinned here so | ||
| * both flavors stay in lockstep. | ||
| * | ||
| * Static hook counters accumulate across directory runs β feature tests assert deltas. | ||
| */ | ||
| #[Test] | ||
| final class FullySkippedClassStub | ||
| { | ||
| public static int $beforeClassCalls = 0; | ||
| public static int $afterClassCalls = 0; | ||
|
|
||
| #[BeforeClass] | ||
| public static function bootCase(): void | ||
| { | ||
| ++self::$beforeClassCalls; | ||
| } | ||
|
|
||
| #[AfterClass] | ||
| public static function shutdownCase(): void | ||
| { | ||
| ++self::$afterClassCalls; | ||
| } | ||
|
|
||
| #[Skip('the whole class case is skipped')] | ||
| public function skipped(): void | ||
| { | ||
| throw new \LogicException('Must never run: the test is skipped.'); | ||
| } | ||
| } |
75 changes: 75 additions & 0 deletions
75
plugin/lifecycle/tests/Stub/FullySkipped/fully_skipped_functions.php
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Tests\Lifecycle\Stub\FullySkipped; | ||
|
|
||
| use Testo\Lifecycle\AfterClass; | ||
| use Testo\Lifecycle\AfterTest; | ||
| use Testo\Lifecycle\BeforeClass; | ||
| use Testo\Lifecycle\BeforeTest; | ||
| use Testo\Test; | ||
| use Testo\Skip; | ||
|
|
||
| /** | ||
| * A fully skipped function-based case: every `#[Test]` function is under `#[Skip]`. Mirrors | ||
| * {@see FullySkippedClassStub} for the function-based shape of the same scenario. Its two tests | ||
| * spell the attribute both ways β `skippedFnOne` with a reason, `skippedFnTwo` without β so neither | ||
| * form leaves the case with an active test. | ||
| * | ||
| * The skipped tests are flagged ahead of the run and stay active, so the | ||
| * {@see \Testo\Lifecycle\Internal\LifecycleInterceptor} sees a case with tests but without a single | ||
| * one to run: none of its hooks fire (the `#[Skip]` contract). | ||
| * | ||
| * Static hook counters accumulate across directory runs β feature tests assert deltas. | ||
| * State is shared through {@see FullySkippedFunctionState} because functions have no `$this`. | ||
| */ | ||
| #[BeforeClass] | ||
| function skippedCaseSetUpClass(): void | ||
| { | ||
| ++FullySkippedFunctionState::$beforeClassCalls; | ||
| } | ||
|
|
||
| #[AfterClass] | ||
| function skippedCaseTearDownClass(): void | ||
| { | ||
| ++FullySkippedFunctionState::$afterClassCalls; | ||
| } | ||
|
|
||
| #[BeforeTest] | ||
| function skippedCaseSetUp(): void | ||
| { | ||
| ++FullySkippedFunctionState::$beforeTestCalls; | ||
| } | ||
|
|
||
| #[AfterTest] | ||
| function skippedCaseTearDown(): void | ||
| { | ||
| ++FullySkippedFunctionState::$afterTestCalls; | ||
| } | ||
|
|
||
| #[Test] | ||
| #[Skip('the whole functional case is skipped')] | ||
| function skippedFnOne(): void | ||
| { | ||
| throw new \LogicException('Must never run: the test is skipped.'); | ||
| } | ||
|
|
||
| #[Test] | ||
| #[Skip] | ||
| function skippedFnTwo(): void | ||
| { | ||
| throw new \LogicException('Must never run: the test is skipped.'); | ||
| } | ||
|
|
||
| /** | ||
| * Call counters for the lifecycle functions above. Not autoloadable β the feature test | ||
| * `require_once`s this file before touching the counters. | ||
| */ | ||
| final class FullySkippedFunctionState | ||
| { | ||
| public static int $beforeClassCalls = 0; | ||
| public static int $afterClassCalls = 0; | ||
| public static int $beforeTestCalls = 0; | ||
| public static int $afterTestCalls = 0; | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.