Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions .github/workflows/isolated-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
outputs:
matrix: ${{ steps.get_json.outputs.json }}

phpunit:
tests:
runs-on: ${{ matrix.os }}
needs: get_packages
strategy:
Expand Down Expand Up @@ -79,5 +79,6 @@ jobs:
cd "packages/${{ matrix.package.basename }}"
composer update --${{ matrix.stability }} --prefer-dist --no-interaction --ignore-platform-reqs

- name: Execute tests
run: phpunit -c "packages/${{ matrix.package.basename }}/phpunit.xml"
- name: Execute PHPUnit tests
run: |
[ ! -f "packages/${{ matrix.package.basename }}/phpunit.xml" ] || phpunit -c "packages/${{ matrix.package.basename }}/phpunit.xml"
2 changes: 1 addition & 1 deletion bin/validate-packages
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ function checkPackage(array $package): void
{
checkPackageFile($package, '.gitattributes');
checkPackageFile($package, 'composer.json');
checkPackageFile($package, 'phpunit.xml');
// checkPackageFile($package, 'phpunit.xml');
// TODO: Issue #426
// checkPackageFile($package, 'README.md');
checkPackageLicense($package);
Expand Down
19 changes: 12 additions & 7 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,12 @@
"psr-discovery/http-client-implementations": "^1.4",
"psr-discovery/http-factory-implementations": "^1.2",
"psr/cache": "^3.0",
"psr/clock": "^1.0.0",
"psr/http-client": "^1.0.0",
"psr/http-factory": "^1.0",
"psr/http-message": "^1.0|^2.0",
"psr/log": "^3.0.0",
"psr/clock": "^1.0",
"psr/container": "^2.0",
"psr/http-client": "^1.0",
"psr/http-factory": "^1.1",
"psr/http-message": "^2.0",
"psr/log": "^3.0",
"rector/rector": "^2.2.5",
"symfony/cache": "^7.3",
"symfony/mailer": "^7.2.6",
Expand Down Expand Up @@ -118,6 +119,7 @@
"tempest/router": "self.version",
"tempest/storage": "self.version",
"tempest/support": "self.version",
"tempest/testing": "self.version",
"tempest/upgrade": "self.version",
"tempest/validation": "self.version",
"tempest/view": "self.version",
Expand Down Expand Up @@ -159,6 +161,7 @@
"Tempest\\Router\\": "packages/router/src",
"Tempest\\Storage\\": "packages/storage/src",
"Tempest\\Support\\": "packages/support/src",
"Tempest\\Testing\\": "packages/testing/src",
"Tempest\\Upgrade\\": "packages/upgrade/src",
"Tempest\\Validation\\": "packages/validation/src",
"Tempest\\View\\": "packages/view/src",
Expand Down Expand Up @@ -196,6 +199,7 @@
"packages/support/src/Str/functions.php",
"packages/support/src/Uri/functions.php",
"packages/support/src/functions.php",
"packages/testing/src/functions.php",
"packages/view/src/functions.php",
"packages/vite/src/functions.php"
]
Expand Down Expand Up @@ -227,6 +231,7 @@
"Tempest\\Router\\Tests\\": "packages/router/tests",
"Tempest\\Storage\\Tests\\": "packages/storage/tests",
"Tempest\\Support\\Tests\\": "packages/support/tests",
"Tempest\\Testing\\Tests\\": "packages/testing/tests",
"Tempest\\Upgrade\\Tests\\": "packages/upgrade/tests",
"Tempest\\Validation\\Tests\\": "packages/validation/tests",
"Tempest\\View\\Tests\\": "packages/view/tests",
Expand All @@ -248,7 +253,7 @@
"fmt": "vendor/bin/mago fmt",
"lint:fix": "vendor/bin/mago lint --fix --format-after-fix",
"style": "composer fmt && composer lint:fix",
"test": "composer phpunit",
"test": "@php tempest test && composer phpunit",
"lint": "vendor/bin/mago lint --potentially-unsafe --minimum-fail-level=note",
"phpstan": "vendor/bin/phpstan analyse src tests --memory-limit=1G",
"rector": "vendor/bin/rector process --no-ansi",
Expand All @@ -266,7 +271,7 @@
"composer rector",
"./bin/validate-packages",
"./tempest discovery:clear --no-interaction",
"composer phpunit",
"composer test",
"composer phpstan"
]
}
Expand Down
1 change: 1 addition & 0 deletions mago.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ excludes = [
"**/*.stub.php",
"**/*.input.php",
"**/*.expected.php",
"packages/testing/tests/ProviderTest.php",
]

[formatter]
Expand Down
2 changes: 1 addition & 1 deletion packages/clock/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"description": "A clock component that handle few simple clock operations.",
"require": {
"php": "^8.5",
"psr/clock": "^1.0.0",
"psr/clock": "^1.0",
"tempest/datetime": "dev-main"
},
"autoload": {
Expand Down
4 changes: 2 additions & 2 deletions packages/http-client/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"minimum-stability": "dev",
"require": {
"php": "^8.5",
"psr/http-client": "^1.0.0",
"psr/http-message": "^1.0|^2.0",
"psr/http-client": "^1.0",
"psr/http-message": "^2.0",
"tempest/container": "dev-main",
"tempest/http": "dev-main",
"tempest/router": "dev-main",
Expand Down
4 changes: 2 additions & 2 deletions packages/http/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
"tempest/container": "dev-main",
"tempest/cryptography": "dev-main",
"laminas/laminas-diactoros": "^3.3",
"psr/http-factory": "^1.0",
"psr/http-message": "^1.0|^2.0",
"psr/http-factory": "^1.1",
"psr/http-message": "^2.0",
"symfony/uid": "^7.1"
},
"autoload": {
Expand Down
2 changes: 1 addition & 1 deletion packages/log/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"require": {
"php": "^8.5",
"monolog/monolog": "^3.7.0",
"psr/log": "^3.0.0",
"psr/log": "^3.0",
"tempest/container": "dev-main"
},
"autoload": {
Expand Down
14 changes: 14 additions & 0 deletions packages/testing/.gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Exclude build/test files from the release
.github/ export-ignore
tests/ export-ignore
.gitattributes export-ignore
.gitignore export-ignore
phpunit.xml export-ignore
README.md export-ignore

# Configure diff output
*.view.php diff=html
*.php diff=php
*.css diff=css
*.html diff=html
*.md diff=markdown
9 changes: 9 additions & 0 deletions packages/testing/LICENSE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
The MIT License (MIT)

Copyright (c) 2024 Brent Roose [email protected]

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
141 changes: 141 additions & 0 deletions packages/testing/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
## A parallel test runner for modern PHP

This package is an experiment in rethinking testing for PHP. It's not intended for use in real-life projects. Some of the core ideas behind this package:

### ✅ A fluent testing API

```php
use Tempest\Testing\Test;

final class ArrayTest
{
#[Test]
public function forget_keys_mutates_array(): void
{
$original = [
'foo' => 'bar',
'baz' => 'qux',
];

Arr\forget_keys($original, ['foo']);

test($original)
->hasCount(1)
->hasKey('baz')
->hasNoKey('foo');
}
}
```

### ✅ Dependency injection support

Use Tempest's container or any PSR-11 compatible one to inject dependencies into your tests.

```php
use Tempest\Testing\Test;

final class BookTest
{
public function __construct(
private Database $database,
) {}

#[Test]
public function book_can_be_created(BookRepository $repository): void
{
// …
}
}
```

```php
use Tempest\Container\Initializer;
use Tempest\Container\Singleton;
use Tempest\Testing\Actions\RunTest;

final class RunTestInitializer implements Initializer
{
#[Singleton]
public function initialize(Container $container): RunTest
{
return new RunTest(container: new YourOwnContainer());
}
}
```

### ✅ Parallel execution

Parallel execution by default instead of an afterthought.

### ✅ Immediate output

Get immediate feedback on test failures while running them.

```console
× // Tempest\Testing\Tests\TestFoo::a
// /Dev/tempest-testing/tests/TestFoo.php:13
// failed asserting that true is false

× // Tempest\Testing\Tests\TestFoo::b
// /Dev/tempest-testing/tests/TestFoo.php:20
// failed asserting that true is false

× // Tempest\Testing\Tests\TestFoo::e
// /Dev/tempest-testing/tests/TestFoo.php:39
// failed asserting that true is false

2 succeeded 3 failed 0 skipped 0.12s
```

### ✅ Compose tests however you like

```php
final class ApplicationTest
{
use TestsEvents, TestsDatbase, TestsHttp;

#[Test]
public function test_before(): void
{
$this->events
->preventPropagation();

$this->http
->post('/books', ['title' => 'Timeline Taxi'])
->assertRedirectTo('/books/timeline-taxi');

$this->database
->assertContains('books', ['title' => 'Timeline Taxi']);
}
}
```

### ✅ Tempest's no-config approach

Structure your tests however you like: in a separate dev namespaces or alongside your production code. Tempest's discovery will find them for you without any configuration on your part.

### ✅ Extensible event-driven architecture

Anything that happens during tests is easy to hook into with your own event listeners.

```php
use Tempest\Container\Singleton;
use Tempest\Console\HasConsole;
use Tempest\EventBus\EventHandler;
use Tempest\Testing\Events\TestFailed;

#[Singleton]
final class TestEventListeners
{
use HasConsole;

#[EventHandler]
public function onTestFailed(TestFailed $event): void
{
$this->error(sprintf('<style="fg-red">%s</style>', $event->name));
$this->writeln(sprintf(' <style="fg-red dim">//</style> <style="fg-red underline">%s</style>', $event->location));
$this->writeln(sprintf(' <style="fg-red dim">//</style> <style="fg-red">%s</style>', $event->reason));
$this->writeln();
}
}
```
27 changes: 27 additions & 0 deletions packages/testing/composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"name": "tempest/testing",
"description": "A parallel test runner for modern PHP",
"license": "MIT",
"minimum-stability": "dev",
"require": {
"php": "^8.5",
"tempest/core": "dev-main",
"tempest/event-bus": "dev-main",
"tempest/console": "dev-main",
"symfony/process": "^7.3",
"psr/container": "^2.0"
},
"autoload": {
"psr-4": {
"Tempest\\Testing\\": "src"
},
"files": [
"src/functions.php"
]
},
"autoload-dev": {
"psr-4": {
"Tempest\\Testing\\Tests\\": "tests"
}
}
}
31 changes: 31 additions & 0 deletions packages/testing/src/Actions/ChunkAndRunTests.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

namespace Tempest\Testing\Actions;

use Tempest\Support\Arr\ImmutableArray;
use Tempest\Testing\Events\TestRunEnded;
use Tempest\Testing\Events\TestRunStarted;
use Tempest\Testing\Events\TestsChunked;
use Tempest\Testing\Runner\TestRunner;

use function Tempest\event;

final class ChunkAndRunTests
{
public function __invoke(ImmutableArray $tests, int $processes): void
{
$chunks = ceil($tests->count() / $processes);

$tests = $tests
->chunk($chunks)
->map(fn (ImmutableArray $tests, int $i) => new TestRunner($i)->run($tests));

event(new TestsChunked($tests->count()));

event(new TestRunStarted());

$tests->map(fn (TestRunner $runner) => $runner->wait());

event(new TestRunEnded());
}
}
Loading
Loading