Brain-WP/BrainMonkey

Expect a stub doesn't work

Opened this issue · 2 comments

I use a stub to make get_post available, which returns a mock of WP_Post:

        $post = Mockery::mock(\WP_Post::class);
        $post->ID = 1;
        $post->post_content = '<!-- wp:heading {"level":1} -->
<h1 class="wp-block-heading">Heading 1</h1>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>This is content.</p>
<!-- /wp:paragraph -->';
        $post->post_status = 'publish';
        $post->post_type = 'page';
        stubs(
            [
                'get_post' => $post,
                'get_queried_object_id' => 1,
            ]
        );

This is my test:

expect('get_post')->once();
$this->assertEquals(true, My_Class::get_instance()->has_content('This is content.'));
$this->assertEquals(true, My_Class::get_instance()->has_content('This is content.'));

My_Class:

<?php
final class My_Class {
	/**
	 * @var		array A list of available content
	 */
	private array $has_content = [];
	
	public function has_content( string $content ): bool {
		// already searched for this content, return stored value
		if ( isset( $this->has_content[ $content ] ) ) {
			return $this->has_content[ $content ];
		}
		
		$post = \get_post( \get_queried_object_id() );
		
		if ( ! $post instanceof WP_Post ) {
			$this->has_content[ $content ] = false;
			
			return false;
		}
		
		// search in post content
		if ( \strpos( $post->post_content, $content ) !== false ) {
			$this->has_content[ $content ] = true;
			
			return true;
		}
		
		return false;
	}
}

Since I want to test whether I already searched for the content, which would result in an early return for the second run, I thought expect('get_post')->once(); would be correct. However, the error message implies it is not:

[Mockery\Exception\InvalidCountException] Method get_post(<Any Arguments>) from Mockery_1 should be called
 exactly 1 times but called 0 times.  

Am I doing wrong here?

As a workaround, I currently test for expect('strpos')->once();, which works when allowing redefining internals for strpos in the patchwork.json.

I think this is failing because you are both doing a stub and an expectation from the same function.

On the same test, you can't do both things to the same function. This should be documented IIRC.

I would suggest to use the expectations only, like this:

class MyTest extends TestCase
{
    public function testHasContentCallGetPostOnce(): void
    {
        $id = random_int(1, 100);

        $content = <<<'TXT'
            <!-- wp:heading {"level":1} -->
            <h1 class="wp-block-heading">Heading 1</h1>
            <!-- /wp:heading -->
            <!-- wp:paragraph -->
            <p>This is content.</p>
            <!-- /wp:paragraph -->
        TXT;

        $post = $this->stubPost($id, ['content' => $content]);

        \Brain\Monkey\Functions\expect('get_queried_object_id')
            ->once()
            ->andReturn($id);

        \Brain\Monkey\Functions\expect('get_post')
            ->once()
            ->with($id)
            ->andReturn($post);

        $class = My_Class::get_instance();

        static::assertTrue($class->has_content($content));
        static::assertTrue($class->has_content($content));
    }


    private function stubPost(int $id = 1, array $properties = []): \WP_Post
    {
        $post = \Mockery::mock(\WP_Post::class);
        $post->ID = $id;
        foreach ($properties as $key => $value) {
            $property = "post_{$key}";
            $post->{$property} = $value;
        }
        $post->post_content ??= '';
        $post->post_type ??= 'page';
        $post->post_status ??= 'publish';

        return $post;
    }
}

Thank you, that worked just fine!

I’ll leave it open since adding it to the documentation would be indeed nice. 🙂