Brain Monkey

repository·master·Indexed 43 days ago

https://github.com/brain-wp/brainmonkey

A PHP testing utility for simplifying unit testing through general-purpose PHP function mocking (monkey patching) and specialized helpers for WordPress extensions. It provides tools to stub functions via `stubs()`, `when()`, and `expect()`, and includes dedicated helpers for stubbing WordPress translation and escaping functions.

Tokens
14.8K
Snippets
46
Records
64
Agent score
55%

What's inside Brain Monkey

  1. What is Brain Monkey and when to use it

    master

    Brain Monkey is a unit test utility for PHP designed to facilitate testing in isolation. It provides two distinct sets of features:

    1. General Function Mocking: A framework-agnostic tool that allows you to mock and test any PHP function. This is useful for any PHP project, regardless of whether it uses a specific framework, and it is compatible with any testing framework (e.g., PHPUnit, PHPSpec).
    2. WordPress-Specific Testing: A specialized set of tools designed specifically for testing WordPress code, which relies heavily on global functions.

    You can use the general function mocking features independently of the WordPress-specific tools.

  2. What is Brain Monkey?

    master

    Brain Monkey is a testing utility for PHP that provides two distinct sets of helpers:

    1. Framework-agnostic tools: These allow you to mock (or monkey patch) and test the behavior of any standard PHP function.
    2. WordPress-specific tools: These are designed specifically to make unit testing WordPress extensions easier by providing WordPress-related testing helpers.
  3. Brain Monkey dependencies and compatibility

    master

    Brain Monkey is framework-agnostic and can work with any testing framework (e.g., PHPUnit or phpspec).

    Core Dependencies

    Brain Monkey automatically installs the following libraries via Composer:

    Note that while Brain Monkey does not require a specific testing framework to function, you will need to install one (like PHPUnit) to actually execute your tests.

  4. Handle `apply_filters` return behavior in Brain Monkey v2

    master

    In Brain Monkey v2, calling expectApplied() on a filter no longer breaks the default WordPress behavior where apply_filters() returns its first argument. You only need to define return values if you explicitly want to change that behavior.

    Important: If you define a return expectation for a specific set of arguments, you must also define return expectations for all other possible argument sets that might be passed to that filter, otherwise the test will fail. To avoid manually defining every possible argument combination, use andReturnFirstArg() combined with Mockery's zeroOrMoreTimes()->withAnyArgs() to create a 'catch-all' expectation.

    // Define a specific return for one case
    Brain\Monkey\Filters\expectApplied('a_filter')->once()->with('Foo')->andReturn('Foo!');
    
    // Use a catch-all to ensure other calls return the first argument instead of failing
    Brain\Monkey\Filters\expectApplied('a_filter')->zeroOrMoreTimes()->withAnyArgs()->andReturnFirstArg();
    
    self::assertSame('Foo!', apply_filters('a_filter', 'Foo')); // pass
    self::assertSame('Bar', apply_filters('a_filter', 'Bar')); // pass
  5. How Brain Monkey handles function mocking

    master

    Brain Monkey solves the problem of testing code that relies on external functions, which standard PHP testing frameworks (like PHPUnit) cannot easily mock or monkey patch.

    It provides the ability to:

    • Define functions if they are not already defined.
    • Enforce specific behaviors for functions (stubs).
    • Set expectations on function execution (mocks).

    Technically, Brain Monkey integrates the function redefinition capabilities of Patchwork with the testing DSL and mechanism of Mockery to provide a succinct, human-readable API.

  6. How `current_filter()`, `doing_action()`, and `doing_filter()` work in tests

    master

    Brain Monkey correctly resolves WordPress hook state functions inside callbacks used for expectations. This allows you to write complex logic within andReturnUsing() or whenHappen() that relies on knowing which hook is currently active.

    • current_filter(): Returns the name of the current hook being processed.
    • doing_action(): Returns true if the current context is an action.
    • doing_filter(): Returns true if the current context is a filter.

    These functions also correctly resolve 'parent' hooks (e.g., if an action is triggered inside a callback for another action, doing_action() will return true for both).

    // Using current_filter() inside a filter callback
    $callback = function() {
        return current_filter() === 'my_class_title' ? 'Title' : 'Content';
    };
    
    Filters\expectApplied('my_class_title')->once()->andReturnUsing($callback);
    Filters\expectApplied('my_class_content')->once()->andReturnUsing($callback);
  7. Understand default expectation behavior in v2

    master

    In Brain Monkey v1, expectations on the number of times an event occurred were not required, defaulting to zeroOrMoreTimes(). This meant tests would pass even if the expected action or filter was never called.

    In Brain Monkey v2, the default expectation has been changed to ->atLeast()->once(). This means that if you call an expectation like Brain\\Monkey\\Actions\\expectAdded('init'), the test will fail if add_action('init') is not called at least once.

  8. Use Brain Monkey to mock WordPress functions

    master

    Brain Monkey provides fine-grained control over WordPress plugin API functions, allowing you to test WordPress code in isolation without requiring a full WordPress bootstrap.

    When Brain Monkey is loaded for tests, it automatically defines several core WordPress functions to prevent fatal errors and enable testing. This includes:

    • Hook-related functions: add_action(), remove_action(), do_action(), did_action(), apply_filters(), add_filter(), etc.
    • Generic functions: __return_true(), __return_false(), absint(), wp_json_encode(), is_wp_error(), etc.
    • Translation and Escaping functions: Available via stubs.

    With the WP hook API, Brain Monkey allows you to:

    1. Test if an action or filter has been added (including frequency and arguments).
    2. Test if an action or filter has been fired (including frequency and arguments).
    3. Execute a callback when an action is fired to inspect passed arguments.
    4. Execute a callback when a filter is applied to inspect arguments and return specific values.
  9. When to use Brain Monkey for testing

    master

    Brain Monkey is designed for writing unit tests for WordPress-specific code (plugins and themes), rather than testing WordPress core itself.

    Use Brain Monkey when you want to:

    1. Run tests without loading the WordPress environment: This ensures tests run much faster.
    2. Test in isolation: By mocking WordPress functions, you can test your System Under Test (SUT) without interference from external dependencies, ensuring that a test failure points directly to a specific culprit in your code.
    3. Avoid fatal errors: If you attempt to run tests that call WordPress functions without the WordPress environment loaded, PHP will throw fatal errors. Brain Monkey prevents this by mocking those functions.
  10. Mocking WordPress functions with Brain Monkey

    master

    Brain Monkey allows you to mock WordPress functions (and WordPress Plugin API functions) just like any other PHP function. This enables you to:

    1. Prevent fatal errors: Mock functions that would otherwise be undefined when WordPress is not loaded.
    2. Verify behavior: Check how specific WordPress functions are called within your code (e.g., verifying arguments or call counts).
  11. Identify Closures in hooks

    master

    Brain Monkey provides a "serialization" mechanism for closures, allowing you to identify them in has_action() or has_filter() using a string representation that mimics the PHP code used to define the closure.

    Rules for Closure Strings:

    • Namespace: Always use the fully qualified name for classes used in type-hints.
    • Supported: Type-hints (including PHP 7+ scalar types), variadic arguments (...$args), and static closures.
    • Ignored: Default argument values and PHP 7+ return type declarations are not included in the serialized string.

    Example Serialization: If you define function( array $foo = [], $bar = null ) : array, it is serialized as 'function ( array $foo, $bar )'.

    // Code to test
    add_filter('the_title', function($title) {
        return $title;
    }, 99);
    
    // Test assertion
    self::assertNotFalse( has_filter('the_title', 'function ($title)' ) );