Plugins activation in WordPress tests 02

Where I decide upon the hook to use for plugin activation in WordPress tests.

A use case

In an earlier post I’ve explored the motivations and context behind the choice of a better action to activate plugins during functional testing.
The context is that of the WP Loader module part of the wp-browser Codeception module.
The issue is a non trivial one; the WordPress automated testing suite flow involves a WordPress installation and the flow is not exactly the standard WordPress one.
Plugin activation involves a redirection and that cannot happen during tests.
In the above mentioned post I’ve reported the dump of the wp_actions global variable right before the first method runs, here it is

Array
(
    [muplugins_loaded] => 1
    [registered_taxonomy] => 10
    [registered_post_type] => 10
    [plugins_loaded] => 1
    [sanitize_comment_cookies] => 1
    [setup_theme] => 1
    [unload_textdomain] => 1
    [load_textdomain] => 3
    [after_setup_theme] => 1
    [auth_cookie_malformed] => 1
    [set_current_user] => 1
    [init] => 1
    [widgets_init] => 1
    [wp_register_sidebar_widget] => 12
    [wp_loaded] => 1
    [populate_options] => 1
    [add_option] => 9
    [add_option_wp_user_roles] => 1
    [added_option] => 9
    [update_option] => 7
    [update_option_blogname] => 1
    [updated_option] => 6
    [update_option_admin_email] => 1
    [add_user_meta] => 13
    [added_user_meta] => 13
    [set_user_role] => 2
    [user_register] => 1
    [update_user_meta] => 2
    [updated_user_meta] => 2
    [add_option_widget_search] => 1
    [add_option_widget_recent-posts] => 1
    [add_option_widget_recent-comments] => 1
    [add_option_widget_archives] => 1
    [update_option_widget_categories] => 1
    [add_option_widget_meta] => 1
    [add_option_sidebars_widgets] => 1
    [update_option_permalink_structure] => 3
    [permalink_structure_changed] => 3
    [generate_rewrite_rules] => 2
    [add_option_rewrite_rules] => 2
    [http_api_curl] => 2
    [http_api_debug] => 2
    [delete_option] => 2
    [delete_option_rewrite_rules] => 2
    [deleted_option] => 2
    [phpmailer_init] => 1
    [wp_install] => 1
    [before_delete_post] => 2
    [delete_term_relationships] => 1
    [deleted_term_relationships] => 1
    [edit_term_taxonomy] => 1
    [edited_term_taxonomy] => 1
    [clean_term_cache] => 1
    [delete_comment] => 1
    [deleted_comment] => 1
    [clean_object_term_cache] => 3
    [clean_post_cache] => 3
    [wp_update_comment_count] => 1
    [edit_post] => 1
    [wp_set_comment_status] => 1
    [transition_comment_status] => 1
    [comment_approved_to_delete] => 1
    [comment_delete_] => 1
    [delete_post] => 2
    [parse_tax_query] => 4
    [parse_query] => 2
    [pre_get_posts] => 2
    [posts_selection] => 2
    [deleted_post] => 2
    [after_delete_post] => 2
    [delete_post_meta] => 1
    [delete_postmeta] => 1
    [deleted_post_meta] => 1
    [deleted_postmeta] => 1
    [clean_page_cache] => 1
)

but what happens doing the same before a second test runs? I’d like to know if the order of the called WordPress actions is changing in any way.
Setting up the test class like this

<?php

class newTest extends \WP_UnitTestCase
{

    protected $backupGlobals = false;

    public function setUp()
    {
        // before
        parent::setUp();

        // your set up methods here
    }

    /**
     * A first test
     */
    public function test_a_first_test()
    {
        xdebug_break();
        $this->assertTrue(true);
    }

    /**
     * A second test
     */
    public function test_a_second_test()
    {
        xdebug_break();
        $this->assertTrue(true);
    }

    public function tearDown()
    {
        // your tear down methods here
        // then
        parent::tearDown();
    }
}

and getting hold of the the dump of the wp_actions global variable at any xdebug_break call I’ve made sure the list of called actions is the same; I’m not pasting it again for the sake of brevity.

An important insight

WordPress is, in the functional tests flow managed by WP Loader, being installed before each test method; this means the wp_install action hook is a better candidate to activate the plugins the user wishes be activated and specified in the Codeception codeception.yml file under the WP Loader module configuration using the activatePlugins setting.
But why not wp_loaded?

Final choice

Looking again at the list of actions fired before the first line of a test method is fired some important phases of a WordPress installation happen after the wp_loaded hook: I’ve commented on the most notable ones.

Array
(
    ...
    [wp_loaded] => 1
    [populate_options] => 1
    [add_option] => 9
    [add_option_wp_user_roles] => 1
    [added_option] => 9
    [update_option] => 7
    [update_option_blogname] => 1
    [updated_option] => 6
    [update_option_admin_email] => 1

    // Admin user is created here
    [add_user_meta] => 13
    [added_user_meta] => 13
    [set_user_role] => 2
    [user_register] => 1
    [update_user_meta] => 2
    [updated_user_meta] => 2

    // Default widgets in theme
    [add_option_widget_search] => 1
    [add_option_widget_recent-posts] => 1
    [add_option_widget_recent-comments] => 1
    [add_option_widget_archives] => 1
    [update_option_widget_categories] => 1
    [add_option_widget_meta] => 1
    [add_option_sidebars_widgets] => 1

    // permalink structure
    [update_option_permalink_structure] => 3
    [permalink_structure_changed] => 3

    // rewrite rules are generated
    [generate_rewrite_rules] => 2
    [add_option_rewrite_rules] => 2
    [http_api_curl] => 2
    [http_api_debug] => 2
    [delete_option] => 2
    [delete_option_rewrite_rules] => 2
    [deleted_option] => 2
    [phpmailer_init] => 1
    [wp_install] => 1
    ...
)

Note that I can hook this activation on unique hooks only and this makes anything below the wp_install action hook not an option (see complete dump above).
The decision is taken for me: I will move plugins activation to the wp_install hook.

Current user can activate plugins

While calls to activate plugins should only happen during the usual plugin activation flow themes and plugins can override the mechanism completely and call the activate_plugin function out of context.
The end result will still be a call to the function

do_action('activate_{$plugin}');

but knowing this it’s not uncommon to see this check in plugins activation functions or methods

if(!current_user_can('activate_plugins')){
    ... exit, abort or swear
}

// else go on

$my_custom_table_query = <<< MYSQL
...

The default user used during tests is the not logged in one, the one with an ID of 0, and a check like the one above would fail as not logged in users cannot activate plugins by definition.
This means the activation method will run but nothing will happen or some emergency maneuver conceived by the plugin develope happen with dreadful results.
To cope with this I’ve modified the code responsible for the plugin activation to set the current user to the administrator: this should ensure activation methods will run smoothly.

Finally, on GitHub

I’ve pushed the modified version to GitHub tagged wp-browser 1.7.4.

I appreciate your input