Skip to content

Commit

Permalink
Detect any test cases which don't call the parent setup/teardown method
Browse files Browse the repository at this point in the history
- Prevent side effects.
- Prompt users to fix issues before code reviews.
  • Loading branch information
lipemat committed Apr 5, 2024
1 parent f29d078 commit 4b32717
Show file tree
Hide file tree
Showing 7 changed files with 254 additions and 8 deletions.
11 changes: 5 additions & 6 deletions dev/wp-unit/phpunit.xml.dist
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Version 1.2.0 -->
<phpunit backupGlobals="false"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/7.5/phpunit.xsd"
colors="true"
stopOnFailure="true"
bootstrap="bootstrap.php"
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/7.5/phpunit.xsd"
colors="true"
bootstrap="bootstrap.php"
>
<php>
<env name="HTTP_HOST" value="starting-point.loc" />
Expand Down
38 changes: 38 additions & 0 deletions dev/wp-unit/tests/Helpers/SetUpBeforeClassTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php
declare( strict_types=1 );

namespace Lipe\WP_Unit\Helpers;


/**
* @author Mat Lipe
* @since April 2024
*
*/
class SetUpBeforeClassTest extends \WP_UnitTestCase {
public static function set_up_before_class() {
}

public static function tear_down_after_class() {
set_private_property( Setup_Teardown_State::class, 'setup_before', true );
parent::tear_down_after_class();
}


public function setUp(): void {
try {
parent::setUp();
} catch ( \LogicException $e ) {
$this->assertSame( 'Test case did not set up properly. Did you forget to call the `parent::set_up_before_class` or `parent::setUpBeforeClass` method?', $e->getMessage() );
} finally {
$this->assertInstanceOf( \LogicException::class, $e );
}
}

public function test_missing_setup_call(): void {
$this->expectException( \LogicException::class );
$this->expectExceptionMessage( 'Test case did not set up properly. Did you forget to call the `parent::set_up_before_class` or `parent::setUpBeforeClass` method?' );

self::set_up();
}
}
36 changes: 36 additions & 0 deletions dev/wp-unit/tests/Helpers/SetUpTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php
declare( strict_types=1 );

namespace Lipe\WP_Unit\Helpers;

/**
* @author Mat Lipe
* @since April 2024
*
*/
class SetUpTest extends \WP_UnitTestCase {
public function set_up() {
// Did not call set_up().
}


public function tear_down() {
try {
parent::tear_down();
} catch ( \LogicException $e ) {
$this->assertSame( 'Test case did not set up properly. Did you forget to call the `parent::set_up` or `parent::setUp` method?', $e->getMessage() );
} finally {
$this->assertInstanceOf( \LogicException::class, $e );
}

set_private_property( Setup_Teardown_State::class, 'setup', true );
}


public function test_missing_setup_call(): void {
$this->expectException( \LogicException::class );
$this->expectExceptionMessage( 'Test case did not set up properly. Did you forget to call the `parent::set_up` or `parent::setUp` method?' );

parent::tear_down();
}
}
33 changes: 33 additions & 0 deletions dev/wp-unit/tests/Helpers/TearDownAfterClassTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php
declare( strict_types=1 );

namespace Lipe\WP_Unit\Helpers;

/**
* @author Mat Lipe
* @since April 2024
*
*/
class TearDownAfterClassTest extends \WP_UnitTestCase {
public static function tear_down_after_class() {
}


public function tear_down(): void {
set_private_property( Setup_Teardown_State::class, 'setup', true );
parent::tear_down();
}


public function test_missing_tear_down_after_class_call(): void {
$this->expectException( \LogicException::class );
$this->expectExceptionMessage( 'Lipe\WP_Unit\Helpers\TearDownAfterClassTest did not tear down after class? Did you forget to call the `parent::tearDownAfterClass` or `parent::tear_down_after_class` method?' );

try {
self::tear_down_after_class();
self::set_up_before_class();
} finally {
set_private_property( Setup_Teardown_State::class, 'tear_down_after_classes', [] );
}
}
}
42 changes: 42 additions & 0 deletions dev/wp-unit/tests/Helpers/TearDownTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php
declare( strict_types=1 );

namespace Lipe\WP_Unit\Helpers;

/**
* @author Mat Lipe
* @since April 2024
*
*/
class TearDownTest extends \WP_UnitTestCase {
public function tear_down() {
// Did not call tear_down().
}


public static function set_up_before_class() {
parent::set_up_before_class();
set_error_handler( function( $errno, $errstr, $errfile, $errline ) {
throw new \ErrorException( $errstr, $errno, 0, $errfile, $errline );
} );
}

public static function tear_down_after_class() {
try {
parent::tear_down_after_class();
} catch ( \ErrorException $e ) {
self::assertSame( 'Test case did not tear down properly. Did you forget to call the `parent::tear_down` or `parent::tearDown` method?', $e->getMessage() );
} finally {
restore_error_handler();
self::assertInstanceOf( \ErrorException::class, $e );
}
}


public function test_missing_tear_down_call(): void {
$this->expectException( \ErrorException::class );
$this->expectExceptionMessage( 'Test case did not tear down properly. Did you forget to call the `parent::tear_down` or `parent::tearDown` method?' );

parent::tear_down_after_class();
}
}
10 changes: 8 additions & 2 deletions includes/abstract-testcase.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Lipe\WP_Unit\Helpers\Doing_It_Wrong;
use Lipe\WP_Unit\Helpers\Global_Hooks;
use Lipe\WP_Unit\Helpers\Hook_State;
use Lipe\WP_Unit\Helpers\Setup_Teardown_State;
use Lipe\WP_Unit\Helpers\Snapshots;
use Lipe\WP_Unit\Traits\Helper_Access;

Expand Down Expand Up @@ -76,6 +77,7 @@ public static function set_up_before_class() {
}

self::commit_transaction();
Setup_Teardown_State::set_up_before_class( $class );
}

/**
Expand All @@ -85,7 +87,7 @@ public static function tear_down_after_class() {
$class = get_called_class();

if ( method_exists( $class, 'wpTearDownAfterClass' ) ) {
call_user_func( array( $class, 'wpTearDownAfterClass' ) );
call_user_func( [ $class, 'wpTearDownAfterClass' ] );
}

if ( ! tests_skip_install() ) {
Expand All @@ -96,6 +98,7 @@ public static function tear_down_after_class() {
self::commit_transaction();

Global_Hooks::instance()->restore_globals();
Setup_Teardown_State::tear_down_after_class( $class );

parent::tear_down_after_class();
}
Expand Down Expand Up @@ -142,13 +145,15 @@ public function set_up() {
$this->start_transaction();
$this->_fill_expected_deprecated();
add_filter( 'wp_die_handler', array( $this, 'get_wp_die_handler' ) );

Setup_Teardown_State::set_up();
}

/**
* After a test method runs, resets any state in WordPress the test method might have changed.
*/
public function tear_down() {
global $wpdb, $wp_the_query, $wp_query, $wp;
global $wpdb, $wp_the_query, $wp_query, $wp, $wp_unit_torn_down;
$wpdb->query( 'ROLLBACK' );
if ( is_multisite() ) {
while ( ms_is_switched() ) {
Expand Down Expand Up @@ -217,6 +222,7 @@ public function tear_down() {

$this->reset_lazyload_queue();

Setup_Teardown_State::tear_down();
}

/**
Expand Down
92 changes: 92 additions & 0 deletions includes/src/Helpers/Setup_Teardown_State.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<?php
declare( strict_types=1 );

namespace Lipe\WP_Unit\Helpers;

/**
* Track the state of the setup and tear doan method to
* insure the parent methods are called and state is reset.
*
*
*
* @author Mat Lipe
* @since 3.8.0
*
*/
class Setup_Teardown_State {
/**
* @var bool
*/
protected static $setup_before = false;

/**
* @var bool
*/
protected static $setup = false;

/**
* @var bool
*/
protected static $tear_down = false;

/**
* @var array<string, bool>
*/
protected static $tear_down_after_classes = [];


public static function set_up_before_class( string $class ): void {
try {
if ( \count( self::$tear_down_after_classes ) > 0 ) {
$classes = \implode( ', ', \array_keys( self::$tear_down_after_classes ) );
throw new \LogicException( $classes . ' did not tear down after class? Did you forget to call the `parent::tearDownAfterClass` or `parent::tear_down_after_class` method?' );
}
} finally {
self::reset();
self::$setup_before = true;
self::$tear_down_after_classes[ $class ] = true;
}
}


public static function set_up(): void {
self::$setup = true;

if ( ! self::$setup_before ) {
throw new \LogicException( 'Test case did not set up properly. Did you forget to call the `parent::set_up_before_class` or `parent::setUpBeforeClass` method?' );
}
}


public static function tear_down(): void {
self::$tear_down = true;

if ( ! self::$setup ) {
throw new \LogicException( 'Test case did not set up properly. Did you forget to call the `parent::set_up` or `parent::setUp` method?' );
}
}


public static function tear_down_after_class( string $class ): void {
unset( self::$tear_down_after_classes[ $class ] );

try {
if ( ! self::$tear_down ) {
trigger_error( 'Test case did not tear down properly. Did you forget to call the `parent::tear_down` or `parent::tearDown` method?', E_USER_ERROR );
} elseif ( ! self::$setup_before ) {
trigger_error( 'Test case did not set up before properly?. Did you forget to call the `parent::set_up_before_class` or `parent::setUpBeforeClass` method?', E_USER_ERROR );
} elseif ( ! self::$setup ) {
trigger_error( 'Test case did not set up properly. Did you forget to call the `parent::set_up` or `parent::setUp` method?', E_USER_ERROR );
}
} finally {
self::reset();
}
}


protected static function reset(): void {
self::$setup_before = false;
self::$setup = false;
self::$tear_down = false;
}
}

0 comments on commit 4b32717

Please sign in to comment.