web-dev-qa-db-fra.com

Rétablir les options par défaut de WordPress après l'exécution d'un test PHPUnit

tl; dr: existe-t-il un moyen d’apporter des modifications aux options WordPress lors de l’exécution d’une suite de tests PHPUnit et de revenir aux modifications par défaut de WordPress avant l’exécution de la suite de tests suivante, sans écrire de fonction de démontage personnalisée?

Je teste mon plugin qui fournit une fonction pour ajouter et supprimer des rôles en même temps. Les utilisateurs de mon plug-in ne devraient exécuter cette fonction qu'une seule fois, mais ils peuvent choisir de ne pas l'exécuter du tout. J'ai donc configuré deux suites de tests (à l'aide de l'échafaudage de WP-CLI) pour tester la situation avant et après la conversion des rôles. Étant donné que l'opération de conversion de rôle apporte des modifications à la base de données, j'ai séparé les tests sur les rôles convertis en leur propre groupe PHPUnit et ai exclu l'exécution de ce groupe par défaut. Pour tester la fonction de changement de rôle, je dois donc appeler PHPUnit avec le drapeau --group role-permissions. Je peux également exécuter les tests sur les rôles WordPress par défaut en appelant PHPUnit sans groupe. Je rencontre un problème lorsque je lance les tests les uns après les autres .

Premièrement, si je lance les tests par défaut ...

$> phpunit
[passes]

... ils passent d'abord. Si je lance ensuite les tests de rôles spéciaux ...

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

... alors ils passent aussi. Mais si je lance à nouveau les tests par défaut après cela ...

$> phpunit
[fails]

... ils ne passent plus. J'ai constaté que cela est dû au fait que les options modifiées par les tests role-permissions sont toujours présentes dans la base de données de test avant que celles par défaut ne soient réexécutées. Le seul moyen de faire passer à nouveau les tests par défaut consiste à régénérer la base de données de tests WordPress par défaut.

Pour convertir les rôles afin que je puisse exécuter les tests role-permissions, j'ai du code dans wpSetUpBeforeClass. Cela ne fonctionne qu'une fois par exécution de PHPUnit, avant que les tests ne soient exécutés, donc cela semble être le bon endroit pour mettre le code. Toutefois, il est clair que le code d’échafaudage test ne restaure pas la table de base de données par défaut wptests_options après chaque exécution.

Existe-t-il un moyen de restaurer les options par défaut dans la base de données après l'exécution de mes tests spéciaux, ou d'exécuter mes tests role-permissions dans leur propre base de données, ou un autre moyen d'éviter les échecs?

Pour référence, les versions simplifiées des fichiers pertinents sont indiquées ci-dessous:

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: existe-t-il un moyen d’apporter des modifications aux options WordPress lors de l’exécution d’une suite de tests PHPUnit et de rétablir les valeurs par défaut de WordPress avant l’exécution de la suite de tests suivante sans écrire de fonction de démontage personnalisée?

Oui et non.

Non, vous ne pouvez pas utiliser le code que vous avez actuellement et attendre ce résultat. La suite de tests utilise des transactions et annule automatiquement la base de données après chaque test. Mais vous apportez vos modifications dans wpSetUpBeforeClass(), qui est exécuté avant le début de la transaction. Tout ce que vous faites dans wpSetUpBeforeClass() vous devez vous nettoyer dans wpTearDownAfterClass(). C'est par conception.

Cependant, vous n'êtes pas obligé d'utiliser wpSetUpBeforeClass(). Vous pouvez simplement placer votre code dans setUp() à la place. En appelant parent::setUp() avant d'effectuer vos modifications, la transaction de base de données aura déjà commencé et vos modifications seront automatiquement annulées à la fin de chaque test.

2
J.D.

Après le commentaire de Mark Kaplun, j'ai creusé un peu dans le code source du test WordPress et trouvé la fonction _delete_all_data qui est exécutée après chaque test:

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" );
}

Comme vous pouvez le constater, cette fonction rétablit (supprime techniquement), seuls les posts, commentaires, conditions et utilisateurs sont supprimés. Les options et autres éléments de configuration du site ne sont pas annulés.

La solution que j'ai trouvée pour résoudre mon problème consiste à sauvegarder l'option des rôles avant de la modifier, puis à la rétablir par la suite. Ce n’est pas la solution que j’aurais préféré, mais elle semble être la meilleure disponible. Tout d'abord, je définis les méthodes d'installation et de démontage de classe qui s'exécutent une fois avant et après l'exécution de tous les tests:

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

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

Ensuite, je définis les fonctions pour créer les nouveaux rôles et les réinitialiser avant. Pour cela, j'ai besoin de variables statiques définies dans la classe.

/**
 * 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();
}

Les variables statiques de la classe peuvent être définies comme suit:

protected static $role_key;
protected static $default_roles;

Maintenant, mes tests réussissent quel que soit l'ordre dans lequel ils s'appellent.

2
Sean