Skip to content

Commit

Permalink
Add parallel unit testing (#582)
Browse files Browse the repository at this point in the history
* Spacing fix

* Adding parallel support

* WIP

* WIP

* Fixing trait

* Getting tests to pass

* WIP

* Clear out

* MVP of parallel testing

* CHANGELOG

* WIP

* Drop 8.1 support and switch to parallel job

* Ensure that an application instance is not created using factory models

* Adding 7.3 support for brianium/paratest

* Make it work

* Switch back to 8.1

* Make it work
  • Loading branch information
srtfisher authored Oct 16, 2024
1 parent 0d5572c commit 55bd5ab
Show file tree
Hide file tree
Showing 15 changed files with 79 additions and 21 deletions.
1 change: 1 addition & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ jobs:
uses: alleyinteractive/.github/.github/workflows/php-tests.yml@main
name: "P${{ matrix.php }} - ${{ matrix.wordpress }} ${{ matrix.multisite && 'Multisite' || '' }} - ${{ matrix.dependencies }}"
with:
command: 'phpunit'
dependency-versions: ${{ matrix.dependencies }}
multisite: ${{ matrix.multisite }}
php: ${{ matrix.php }}
Expand Down
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased
## v1.1.0 - 2024-09-23

### Added

Expand All @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Allow the block factory to override text when generating blocks.
- Added new `defer()` helper.
- Added `Cache::flexible()` method to add SWR support to the cache.
- Added support for parallel unit testing with `brianium/paratest` (in beta).
- Added dynamic creation of post type/taxonomy factories.
- Added `Reset_Server` trait to reset the server between tests.
- Add `with_https()` to control if the request being tested is over HTTPS.
Expand Down
4 changes: 4 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
"phpstan/phpstan": "1.10.67",
"phpunit/phpunit": "^9.3.3 || ^10.0.7 || ^11.0",
"rector/rector": "^1.0",
"spatie/ray": "^1.41",
"squizlabs/php_codesniffer": "^3.7",
"symplify/monorepo-builder": "^10.3.3",
"szepeviktor/phpstan-wordpress": "^1.3"
Expand Down Expand Up @@ -156,5 +157,8 @@
"@rector",
"@phpunit"
]
},
"suggest": {
"brianium/paratest": "Run PHPUnit tests in parallel"
}
}
2 changes: 1 addition & 1 deletion monorepo-builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
[
ComposerJsonSection::REQUIRE => [
'alleyinteractive/composer-wordpress-autoloader' => '^1.0',
'php' => '^8.1',
'php' => '^8.2',
],
ComposerJsonSection::REQUIRE_DEV => [
'alleyinteractive/alley-coding-standards' => '^2.0',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
namespace Mantle\Database\Factory\Concerns;

use InvalidArgumentException;
use Mantle\Application\Application;
use Mantle\Contracts\Application;
use Mantle\Container\Container;
use Mantle\Database\Factory;
use Mantle\Database\Model;
Expand Down Expand Up @@ -152,9 +152,13 @@ public static function default_factory_name( string $model_name ): string {
*/
protected static function app_namespace(): string {
try {
return str(
Container::get_instance()->make( Application::class )->get_namespace()
)->rtrim( '\\' )->append( '\\' );
$container = Container::get_instance();

if ( $container instanceof Application ) {
return str( $container->get_namespace() )->rtrim( '\\' )->append( '\\' );
}

return 'App\\';
} catch ( \Throwable ) {
return 'App\\';
}
Expand Down
4 changes: 2 additions & 2 deletions src/mantle/facade/class-cache.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
/**
* Cache Facade
*
* @method static mixed get(string $key, mixed $default = null)
* @method static mixed|null get(string $key, mixed $default = null)
* @method static iterable get_multiple(iterable $keys, mixed $default = null)
* @method static mixed flexible(string $key, int|\DateInterval|\DateTimeInterface|null $stale, int|\DateInterval|\DateTimeInterface|null $expire, callable $callback)
* @method static mixed|null flexible(string $key, int|\DateInterval|\DateTimeInterface|null $stale, int|\DateInterval|\DateTimeInterface|null $expire, callable $callback)
* @method static mixed|null pull(string $key, mixed $default = null)
* @method static bool set(string $key, mixed $value, null|int|\DateInterval $ttl = null)
* @method static bool set_multiple(iterable $values, null|int|\DateInterval|\DateTimeInterface $ttl = null)
Expand Down
34 changes: 34 additions & 0 deletions src/mantle/testing/class-utils.php
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,15 @@ public static function setup_configuration(): void {
defined( 'WP_PHP_BINARY' ) || define( 'WP_PHP_BINARY', 'php' );
defined( 'WPLANG' ) || define( 'WPLANG', '' );

// Setup the table prefix when running in parallel.
if ( static::is_parallel() && $token = static::parallel_token() ) {
$table_prefix .= "para_{$token}_"; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited

if ( static::is_debug_mode() ) {
static::info( "Using parallel table prefix: {$table_prefix}" );
}
}

// phpcs:enable WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedConstantFound
}

Expand Down Expand Up @@ -515,4 +524,29 @@ public static function handle_shutdown(): void {

exit( 1 );
}

/**
* Check if the current test run is parallel with paratest.
*/
public static function is_parallel(): bool {
return ! empty( static::parallel_token() );
}

/**
* Retrieve the parallel token for the current test run.
*
* @return string
*/
public static function parallel_token(): ?string {
return static::env( 'TEST_TOKEN', null );
}

/**
* Check if the current test run is the paratest bootstrap.
*
* The parallel token will not be set in the initial bootstrap.
*/
public static function is_parallel_bootstrap(): bool {
return empty( static::parallel_token() ) && isset( $_SERVER['SCRIPT_NAME'] ) && str_contains( (string) $_SERVER['SCRIPT_NAME'], 'paratest' ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
}
}
1 change: 1 addition & 0 deletions src/mantle/testing/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"nunomaduro/collision": "For better PHPUnit printing.",
"mantle-framework/console": "Required to assert console commands.",
"mantle-framework/testkit": "For running tests against a WordPress install without Mantle",
"brianium/paratest": "For running tests in parallel.",
"phpunit/phpunit": "Required to use assertions and run tests."
},
"config": {
Expand Down
6 changes: 6 additions & 0 deletions src/mantle/testing/wordpress-bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,12 @@
define( 'WP_DEFAULT_THEME', Utils::env( 'WP_DEFAULT_THEME', 'default' ) );
}

// Bail early if this is this is a parallel test bootstrap: installation of
// WordPress is handled in each process.
if ( Utils::is_parallel_bootstrap() ) {
return;
}

$wp_theme_directories = [];
$installing_wp = defined( 'WP_INSTALLING' ) && WP_INSTALLING;

Expand Down
2 changes: 2 additions & 0 deletions src/mantle/testkit/class-application.php
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ class Application extends Container implements Application_Contract {
* @param string $root_url Root URL of the application.
*/
public function __construct( string $base_path = '', string $root_url = null ) {
static::$instance = $this;

if ( empty( $base_path ) && defined( 'MANTLE_BASE_DIR' ) ) {
$base_path = \MANTLE_BASE_DIR;
}
Expand Down
6 changes: 4 additions & 2 deletions tests/Database/Factory/UnitTestingFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,9 @@ protected function shim_test( string $class_name, string $property ) {
* @dataProvider dataprovider_factory
*/
#[DataProvider( 'dataprovider_factory' )]
public function test_dataprovider_factory( $post ) {
public function test_dataprovider_factory( callable $fn ) {
$post = $fn();

$this->assertInstanceOf( \WP_Post::class, $post );
$this->assertStringContainsString(
'<!-- wp:paragraph',
Expand All @@ -307,7 +309,7 @@ public function test_dataprovider_factory( $post ) {

public static function dataprovider_factory(): array {
return [
'example' => [ static::factory()->post->create_and_get() ],
'example' => [ fn () => static::factory()->post->create_and_get() ],
];
}

Expand Down
16 changes: 7 additions & 9 deletions tests/Database/Query/PostQueryBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ public function test_term_and_query() {
public function test_term_query_from_term_model() {
$post_id = $this->get_random_post_id();
$term_other = static::factory()->term->create();
$term = Testable_Tag::find( static::factory()->term->create() );
$term = Testable_Post_Tag::find( static::factory()->term->create() );

wp_set_object_terms( $post_id, $term->id(), $term->taxonomy(), true );

Expand Down Expand Up @@ -735,15 +735,13 @@ class Another_Testable_Post extends Post {
public static $object_name = 'example-post-type';
}

class Testable_Tag extends Term {
class Testable_Post_Tag extends Term {
public static $object_name = 'post_tag';
}

if ( PHP_VERSION_ID >= 80100 ) {
enum Testable_Meta_Values: string {
case Meta_Key_A = 'meta-key-a';
case Meta_Key_B = 'meta-key-b';
case Meta_Value_A = 'meta-value-a';
case Meta_Value_B = 'meta-value-b';
}
enum Testable_Meta_Values: string {
case Meta_Key_A = 'meta-key-a';
case Meta_Key_B = 'meta-key-b';
case Meta_Value_A = 'meta-value-a';
case Meta_Value_B = 'meta-value-b';
}
4 changes: 4 additions & 0 deletions tests/Database/Query/TermQueryBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,10 @@ protected function get_random_term_id( $args = [] ): int {
}
}

class Testable_Tag extends Term {
public static $object_name = 'post_tag';
}

class Testable_Category extends Term {
public static $object_name = 'category';
}
Expand Down
3 changes: 2 additions & 1 deletion tests/Support/StringableTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@

use Carbon\Carbon;
use Mantle\Support\Collection;
// use Mantle\Support\HtmlString;
use Mantle\Support\Stringable;
use PHPUnit\Framework\TestCase;

require_once __DIR__ . '/StrTest.php';

class StringableTest extends TestCase {

/**
Expand Down
2 changes: 1 addition & 1 deletion tests/bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
// Enable debugging flag for local development on the testing framework.
// define( 'MANTLE_TESTING_DEBUG', true );

(new \NunoMaduro\Collision\Provider)->register();
( new \NunoMaduro\Collision\Provider() )->register();

\Mantle\Testing\manager()
->maybe_rsync_plugin()
Expand Down

0 comments on commit 55bd5ab

Please sign in to comment.