How to properly test a method that is called by an action hook

The constructor in my class adds an action to the ‘init’ action hook:

class My_Custom_Post_Type {

    function __construct( $type ) {
        $this->type = $type;
        add_action( 'init', array( $this, 'register_my_type' );
    }

    function register_my_type() {
        register_post_type( $this->type);
    } 
}    

I was trying to test this in a unit test and finally realized that there was no reason for the ‘init’ action hook to fire. Is there any way to test that the register_my_type method is being called and that a new post type is being registered?

Edit:

The main source of my confusion came from not understanding the relationship between phpunit, the WordPress test suite, my class code, and my test code.

What I now understand is that with the WordPress developer tools installed in the way the is suggested by this tutorial or through WP-CLI, a call to phpunit loads a bootstrap file which then hooks the plugin into the muplugins_loaded action hook. The bootstrap file then loads the WordPress test environment. After WordPress has finished loading the init hook is run. Plugin code that makes use of init should work as expected.

Tests are mostly just normal code that is making use of available functions or classes. What is different about them is that they are written as methods of a class that extends WP_UnitTestCase which extends PHPUnit_Framework_TestCase. Being wrapped in this class gives the tests access to assertions and object factories, and runs setUp and tearDown methods so that each test is independent of the others.

1 Answer
1

Let me start by saying that the init action is called when WordPress is loaded, before the tests are run. So it you are having your plugin/theme loaded with WordPress (by hooking into muplugins_loaded, for example), the register_my_type() method should be getting called, if your constructor is being called before init. (If you aren’t loading your plugin that way, have a look at this tutorial.)

So in your unit tests you should be able to do this:

$this->assertTrue( post_type_exists( 'my_post_type' ) );

You could also use get_post_type() to check that the post type was registered with the correct arguments. (This is the same sort of thing I do to check that my shortcodes are registered.)

I wouldn’t worry about trying to test that the method was called, although you could check the list of actions in $wp_actions to see if it was hooked up properly. You could also set a class property as a flag when it is called, but I really think all of that’s overkill. When it comes to things hooked to actions like init, especially a function like this which only needs to run once, functional tests are your best bet. Check that they’ve done what they’re supposed to do, rather than focusing on trying to test a single unit like whether the method was called.

That said, you could also test that the method registers the post type by deregistering the post type and manually calling the method. WordPress doesn’t appear to provide a deregister_post_type() function, so you’d have to mess with the $wp_post_types global directly. You could delete your post type from that and then call your method and check if it is registered again.

Leave a Comment