web-dev-qa-db-ja.com

PHPUnitテストの実行後にWordPressのデフォルトオプションを元に戻す

tl; dr:PHPUnitテストスイートの実行中にWordPressのオプションを変更し、カスタムの破棄関数を書かずに次のテストスイートを実行する前にWordPressのデフォルトに戻す方法はありますか?

私は自分のプラグインをテストしています。これはいくつかの役割を一度に追加および削除する機能を提供します。私のプラグインのユーザーは、この関数を一度だけ実行すればよいのですが、まったく実行しないことを選択できるので、ロールを変換する前後の状況をテストするために2つのテストスイートを設定しました。ロール変換操作によってデータベースが変更されるため、変換したロールのテストを独自のPHPUnitグループに分け、このグループをデフォルトで実行しないようにしました。ロール変更機能をテストするには、PHPUnitを--group role-permissionsフラグ付きで呼び出す必要があります。グループなしでPHPUnitを呼び出すことで、デフォルトのWordPressロールでテストを実行することもできます。 テストを次々と実行すると問題が発生します

まず、デフォルトのテストを実行すると….

$> phpunit
[passes]

...彼らは最初に合格しました。それから私が特別な役割テストを実行するならば...

$> phpunit --group role-permissions
[passes]

…そして彼らはまた通ります。しかし、その後、デフォルトのテストをもう一度実行すると...

$> phpunit
[fails]

...彼らはもう合格しません。これは、role-permissionsテストによって変更されたオプションが、デフォルトのオプションが再度実行される前にテストデータベースに残っているためです。デフォルトのテストに合格するための唯一の方法は、デフォルトのWordPressテストデータベースを再生成することです。

role-permissionsテストを実行できるようにロールを変換するために、wpSetUpBeforeClassにいくつかのコードがあります。テストが実行される前に、これはPHPUnitの実行ごとに一度だけ実行されるので、これはコードを置くのに適した場所のようです。ただし、テスト用の足場コードでは、実行ごとにデフォルトのwptests_optionsデータベーステーブルが復元されないことは明らかです。

私の特別なテストを実行した後にデータベースのデフォルトのオプションを復元する方法、あるいは自分のデータベースで私のrole-permissionsテストを実行する方法、または他の方法で失敗を防ぐ方法はありますか?

参考のために、関連ファイルの簡略版を以下に示します。

tests/test-default-roles.php

/**
 * // no special group
 */
class OtherTests extends WP_UnitTestCase {    
    public function test_default_roles() {
        // test stuff with the default WordPress roles
    }
}

tests/test-new-roles.php

/**
 * @group role-permissions
 */
class RoleTests extends WP_UnitTestCase {
    /**
     * Convert roles before running any test
     * (this changes the wp_settings table)
     */
    public static function wpSetUpBeforeClass( $factory ) {
        // convert roles
        global $my_tool;
        $my_tool->convert_roles();
    }

    public function test_new_roles() {
        // test some stuff to do with the converted roles
    }
}

phpunit.xml

...
<testsuites>
    <testsuite>
        <directory prefix="test-" suffix=".php">./tests/</directory>
    </testsuite>
</testsuites>
<groups>
    <exclude>
        <!-- exclude role conversion tests from running by default -->
        <group>role-permissions</group>
    </exclude>
</groups>
...
3
Sean

tl; dr:PHPUnitテストスイートの実行中にWordPressオプションを変更し、カスタムの破棄関数を書かずに次のテストスイートを実行する前にWordPressのデフォルトに戻す方法はありますか

はいといいえ。

いいえ、現在持っているコードを使用することはできず、この結果を期待することはできません。テストスイートはトランザクションを使用し、各テスト後に自動的にデータベースをロールバックします。しかし、あなたはトランザクションが始まる前に実行されるwpSetUpBeforeClass()にあなたの変更を加えています。 wpSetUpBeforeClass()ですることはすべてwpTearDownAfterClass()で自分自身を片付ける必要があります。これは仕様によるものです。

しかし、wpSetUpBeforeClass()を使う必要はありません。代わりにコードをsetUp()に配置することもできます。変更を加える前にparent::setUp()を呼び出すことで、データベーストランザクションはすでに開始されているので、各テストが完了した後に変更は自動的にロールバックされます。

2
J.D.

Mark Kaplun氏のコメントの後、私はWordPressテストのソースコードを少し掘り下げ、各テストの後に実行される _delete_all_data関数 を見つけました。

function _delete_all_data() {
    global $wpdb;
    foreach ( array(
        $wpdb->posts,
        $wpdb->postmeta,
        $wpdb->comments,
        $wpdb->commentmeta,
        $wpdb->term_relationships,
        $wpdb->termmeta,
    ) as $table ) {
        $wpdb->query( "DELETE FROM {$table}" );
    }
    foreach ( array(
        $wpdb->terms,
        $wpdb->term_taxonomy,
    ) as $table ) {
        $wpdb->query( "DELETE FROM {$table} WHERE term_id != 1" );
    }
    $wpdb->query( "UPDATE {$wpdb->term_taxonomy} SET count = 0" );
    $wpdb->query( "DELETE FROM {$wpdb->users} WHERE ID != 1" );
    $wpdb->query( "DELETE FROM {$wpdb->usermeta} WHERE user_id != 1" );
}

ご覧のとおり、この機能は投稿、コメント、用語、ユーザーのみが削除されます(技術的に削除)。オプションやその他のサイト設定は元に戻されません。

私が自分の問題を解決するために見つけた方法はそれを変更する前にロールのオプションをバックアップし、その後それを元に戻すことです。これは私が好むであろう解決策ではありませんが、それは利用可能な最良の解決策のようです。まず、すべてのテストが実行される前後に1回実行されるクラスセットアップメソッドとティアダウンメソッドを定義します。

public static function wpSetUpBeforeClass( $factory ) {
    self::convert_roles();
}

public static function wpTearDownAfterClass() {
    self::reset_roles();
}

それから、新しいロールを作成し、以前にそれらをデフォルトにリセットするための関数を定義します。これらのために、私はクラスで定義されたいくつかの静的変数が必要です。

/**
 * Convert user roles for the tests in this class.
 * 
 * This only works when the current blog is the one being tested.
 */
private static function convert_roles() {
    global $wpdb, $my_tool;

    // role settings name in options table
    self::$role_key = $wpdb->get_blog_prefix( get_current_blog_id() ) . 'user_roles';

    // copy current roles
    self::$default_roles = get_option( self::$role_key );

    // convert roles
    $my_tool->convert_roles();
}

/**
 * Reset changes made to roles
 */
private static function reset_roles() {
    update_option( self::$role_key, self::$default_roles );

    // refresh loaded roles
    self::flush_roles();
}

/**
 * From WordPress core's user/capabilities.php tests
 */
private static function flush_roles() {
    // we want to make sure we're testing against the db, not just in-memory data
    // this will flush everything and reload it from the db
    unset( $GLOBALS['wp_user_roles'] );
    global $wp_roles;
    $wp_roles = new WP_Roles();
}

クラス内の静的変数は次のように定義できます。

protected static $role_key;
protected static $default_roles;

今私のテストはそれらが呼ばれる順序に関係なく合格します。

2
Sean