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.