Brain-WP/BrainMonkey

How to set expectations for inside anonymous functions

KeironLowe opened this issue · 2 comments

Apologies if this is mentioned in the documentation, I've looked but couldn't see anything that would help. Given the following code, how would I go about testing that the code within the anonymous function?

public function register(): void
{
    add_action('init', function () {
    
        // Register the post type.
        register_post_type($this->postType, $this->arguments);
    
        // Add the columns
        if(isset($this->columns)) {
            $this->columns->register();
        }
    });
}

I've tried the following, the assertTrue works fine, but the test fails at expect('register_post_type')->once();.

expect('register_post_type')->once();

$this->createInstance()->register();

$this->assertTrue(has_action('init', 'function ()'));

Hi,

Brain Monkey does not execute the code that you add to hooks, because to do that it would need to "emulate" WordPress and that's not its scope.

You can imagine Brain Monkey as something that counts how many times add_action / do_action / add_filter / apply_filters are called and with arguments.

That said, there's the possibility to use whenHappen() to sort-of hook when one of these functions is called and do "things" with the passed argument. Consider the passed argument to your add_action is a callback, you could execute it.

For example, assuming a SUT like this:

class Sut {

    private $postType;
    private $arguments;
    private $columns;

    public function __construct(string $postType, array $arguments, ?Columns $columns)
    {
        $this->postType = $postType;
        $this->arguments = $arguments;
        $this->columns = $columns;
    }

    public function register(): void
    {
        add_action('init',
            function () {

                // Register the post type.
                register_post_type($this->postType, $this->arguments);

                // Add the columns
                if (isset($this->columns)) {
                    $this->columns->register();
                }
            });
    }
}

You could test in like this:

    public function testRegister()
    {
        $postType = 'post';
        $arguments = ['public' => true];

        $columns = \Mockery::mock('Columns');
        $columns->shouldReceive('register')->once()->withNoArgs();

        Brain\Monkey\Functions\expect('register_post_type')
            ->once()
            ->with($postType, $arguments);

        \Brain\Monkey\Actions\expectAdded('init')
            ->once()
            ->with('function ()')
            ->whenHappen(static function (callable $callback): void {
                // This executes the callback passed as argument to add_action('init')
                $callback();
            });

        $instance = new Sut($postType, $arguments, $columns);
        $instance->register();
    }

Note 1

Documentation for whenHappen can be found here: https://giuseppe-mazzapica.gitbook.io/brain-monkey/wordpress-specific-tools/wordpress-hooks-done#respond-to-actions

Note 2

While this work, usually tests start to become a lot complex when doing things like that. Probably you can consider to don't add hooks inside register() but use register() as hook callback.
That is having a method like thins:

    public function register(): void
    {
         // Register the post type.
        register_post_type($this->postType, $this->arguments);

       // Add the columns
       if (isset($this->columns)) {
          $this->columns->register();
       }
    }

And then in the place where you currently do:

$instance->register();

you could do instead:

add_action('init', [$instance, 'register']);

This way you could test the register method much more easily, and regarding where the hook is added, you just need to test the hook is actually called with the right callback, and Brain Monkey is perfect for that:

\Brain\Monkey\Actions\expectAdded('init')->once()->with([$instance, 'register']);

Perfect thank you, in my case whenHappen was what I needed.