Brain-WP/BrainMonkey

Adding Monkey\setUp() and tearDown() fails otherwise passing tests

dingo-d opened this issue · 3 comments

In the library, I'm writing tests for, I'm writing unit tests using Pest. The tests will check if the WP-CLI command will do their tasks correctly (mainly creating correct classes, doing some search-replace stuff, etc.).

You can check the PR I'm currently working on here: infinum/eightshift-libs#127

So, the test looks like this (I'm omitting the whole thing for brevity)

<?php

namespace Tests\Unit\CustomPostType;

use Brain\Monkey;
use EightshiftBoilerplate\CustomPostType\PostTypeExample;
use EightshiftLibs\CustomPostType\PostTypeCli;

use function Tests\deleteCliOutput;

/**
 * Mock before tests.
 */
beforeEach(function () {
	Monkey\setUp();

	$wpCliMock = \Mockery::mock('alias:WP_CLI');

	$wpCliMock
		->shouldReceive('success')
		->andReturnArg(0);

	$wpCliMock
		->shouldReceive('error')
		->andReturnArg(0);

});

/**
 * Cleanup after tests.
 */
afterEach(function () {
	$output = dirname(__FILE__, 3) . '/cliOutput';

	deleteCliOutput($output);

	Monkey\tearDown();
});


test('Custom post type CLI command will correctly copy the Custom post type class with defaults', function () {
	$cpt = new PostTypeCli('boilerplate');
	$cpt([], $cpt->getDevelopArgs([]));

	// Check the output dir if the generated method is correctly generated.
	$generatedCPT = file_get_contents(dirname(__FILE__, 3) . '/cliOutput/src/CustomPostType/ProductPostType.php');

	$this->assertStringContainsString('class ProductPostType extends AbstractPostType', $generatedCPT);
	$this->assertStringContainsString('admin-settings', $generatedCPT);
	$this->assertStringNotContainsString('dashicons-analytics', $generatedCPT);
});


test('Register method will call init hook', function () {
	(new PostTypeExample())->register();

	$this->assertSame(10, has_action('init', 'EightshiftBoilerplate\CustomPostType\PostTypeExample->postTypeRegisterCallback()'));
});

The beforeEach and afterEach are analogous to setUp and tearDown methods, so I placed the Brain Monkey setUp and tearDown methods there as well. I'm also mocking some WP-CLI methods, and I need to delete the output of the directory, where my created class will be after I invoke my WP-CLI class (every class does something).

The first test checks that the output of the command is correct. The second test will just make sure we have correctly registered hooks in the example classes (which are used for creating new classes with WP-CLI commands).

If I run this test, I'll get

 Whoops\Exception\ErrorException

  fopen(/Users/denis.zoljom/Projects/composer.json): failed to open stream: No such file or directory

  at vendor/antecedent/patchwork/src/CodeManipulation/Stream.php:51
     47▕             $this->wrap();
     48▕             return true;
     49▕         }
     50▕         if (isset($this->context)) {
  ➜  51▕             $this->resource = fopen($path, $mode, $options, $this->context);
     52▕         } else {
     53▕             $this->resource = fopen($path, $mode, $options);
     54▕         }
     55▕         $this->wrap();

      +1 vendor frames
  2   [internal]:0
      Patchwork\CodeManipulation\Stream::stream_open("/Users/denis.zoljom/Projects/composer.json", "rb")

  3   src/Cli/AbstractCli.php:646
      file_get_contents("/Users/denis.zoljom/Projects/composer.json")

If I remove the Brain Monkey setUp and tearDown methods, then the test passes, but the last test will fail, because PHP doesn't know anything about add_action method (which is expected).

Do you maybe know why this is happening? I guess I can just separate the hook checking test in a separate test, and that should work, but adding these actually made my other tests fail, which I'm really not sure why 🤷🏼‍♂️ .

Pest also has this Helpers.php file where you can add some global helpers you want in every test, and there I've put things like

use Brain\Monkey\Functions;

// Mock WP functions
Functions\stubTranslationFunctions();
Functions\stubEscapeFunctions();

Is it possible that the Monkey\tearDown() is somehow conflicting with that? Which is why the rest of the test will fail?

EDIT

So I've separated the hooks tests into separate files, then the CPT tests passed, but the rest of them failed. So I commented out the separated test with the hook, where I am using the teardown and setup methods, and then all tests pass.

Screenshot 2021-01-16 at 18 41 21
Screenshot 2021-01-16 at 18 41 52

I've also noticed that this seems to happen only when the functionality from the Brain Monkey utility is being used. For instance in the test this will pass:

<?php

namespace Tests\Unit\CustomPostType;

use Brain\Monkey;
use EightshiftBoilerplate\CustomPostType\PostTypeExample;

/**
 * Mock before tests.
 */
beforeEach(function () {
	Monkey\setUp();
});

/**
 * Cleanup after tests.
 */
afterEach(function () {
	Monkey\tearDown();
});


test('Test mocks', function() {
	var_dump(get_template_directory());
});

test('Register method will call init hook', function () {
	(new PostTypeExample())->register();

	$this->assertSame(10, has_action('init', 'EightshiftBoilerplate\CustomPostType\PostTypeExample->postTypeRegisterCallback()'));
});

And I'll get the correct dump of the get_template_directory() that I've mocked in my Helpers.php as

use Brain\Monkey\Functions;

Functions\when('get_template_directory')->justReturn(dirname(__FILE__) . '/data');

while if I place the Test mocks after the Register method will call init hook test, I'll get a failure

• Tests\CustomPostType\CustomPostTypeExampleTest > Test mocks
   Whoops\Exception\ErrorException

  "get_template_directory" is not defined nor mocked in this test.

  at vendor/brain/monkey/src/Expectation/FunctionStub.php:52
     48▕         );
     49▕      }
     50▕ }
     51▕ PHP;
  ➜  52▕         eval($function);
     53▕     }
     54▕
     55▕     /**
     56▕      * @return string

      +1 vendor frames
  2   tests/CustomPostType/CustomPostTypeExampleTest.php:30
      get_template_directory()

Any ideas what could be the issue @gmazzap ?

Turns out it was clearing out the helpers set in the pest framework, which is a bit odd, but I managed to circumvent it.