Skip to content

WordPress hook functions don't behave as expected #49

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
XedinUnknown opened this issue Jun 19, 2019 · 5 comments
Closed

WordPress hook functions don't behave as expected #49

XedinUnknown opened this issue Jun 19, 2019 · 5 comments
Labels

Comments

@XedinUnknown
Copy link

The Problem

Sometimes, it is necessary to test that when a filter is applied, it actually gives the correct result. In these cases, the SUT will often add the filter, which may be a closure. It is not possible to make a functional test of such a SUT.

Possible Cause

Contrary to the documentation, mocked functions like add_filter() and apply_filters() don't actually work in the way their WP equivalents do. Examples:

Example 1

Note that the returning value of those functions (most of the times) will work out of the box as you might expect.

Example 2

Yet again, Brain Monkey, when possible, tries to make WordPress functions it redefines behave in the same way of real WordPress functions.

In reality, when calling apply_filters() from the test, callbacks added with add_filter(), e.g. from the SUT, don't actually run. Thus, the functional test may be completely impossible without duplicating some of the code in the SUT.

Suggested Solution

I see two possible ways of addressing this:

  1. Make these mocked functions optionally behave like their WP counterparts, i.e. run the actual callbacks and return results.
  2. Explicitly mention in the documentation that they will not run the added callbacks.
@gmazzap
Copy link
Contributor

gmazzap commented Jun 24, 2019

Brain Monkey is a mocking library, not a replacement for WP hook system.

That means that it aim to test that, in the regard of WP hooks, specific hooks are added or "applied" a given number of times, with given (kind of) arguments.

To make a comparison PHPUnit mock builder (or Mockery, or any other mock library) allows to do the same for class methods: when creating a mock for a method, the way the "original" method works is completely overridden and if that method is used in another method which expect given return type, that return type has to manually be mocked, and the compliance of mocked return value with the "original" method value is up to the tester.

That to say that:

Make these mocked functions optionally behave like their WP counterparts

is not a desired solution for Brain Monkey and I've no will to do it.

Regarding:

Explicitly mention in the documentation that they will not run the added callbacks

surely documentation can always be improved and I agree that the two pointed sentences in docs can be confusing.

@gmazzap gmazzap added the Docs label Jun 24, 2019
@XedinUnknown
Copy link
Author

With regard your PHPUnit comparison, mocking libraries allow you to choose what to mock, and what to keep. Writing functional tests is mostly about mocking your class's dependencies, not about mocking all of the methods except the one you're running - that would be a unit test. So, in this case, I am trying to mock some of the stuff, and leave other stuff as is - specifically, I want to check if my class works correctly in general, not whether it adds a specific handler: cannot do that even if I wanted to, because the handler is a closure, to which the test has no access. So, I believe that having handlers be run could be a useful and powerful addition to this library, because surely I am not the only person who wants to run a functional test of a class that reacts to a WordPress event.

Alternatively, it would be cool if BrainMonkey could somehow be extended to provide this functionality in a third-party package. Is there a recommended approach to extending BrainMonkey components? I imagine that in my case, I would need to extend and replace HookExpectationExecutor.

In any case, correcting the documentation is IMO an acceptable solution as well, so I am happy to see that you have taken it into consideration. Thank you!

gmazzap added a commit that referenced this issue Jul 22, 2019
API:
- Added `Functions\stubWpTranslationFunctions()`
- Added `Functions\stubWpEscapeFunctions()`
- Make sure default params are used and checked when adding or removing hooks
- Added `Actions\expectRemoved` and `Filters\expectRemoved` (see #45)

Tests:
- re-structured tests folder and test suites
- deleted bootstrap file and rely on autoload-dev only
- improced
- fixed minor tests issues
- added basic functional tests
- added tests for new features

Docs:
- some improvements in various files (ses #49)
- added docs for new features

Others:
- Improved some doc blocks and removed all `@throws` occurrences
- Fixed some end-of-line inconsistencies
@gmazzap
Copy link
Contributor

gmazzap commented Jul 22, 2019

mocking libraries allow you to choose what to mock, and what to keep... I want to check if my class works correctly in general...

Problem is that WordPress is not there. When you're testing a WordPress library making use of WP hook functions, your production code, for good reasons, expect WordPress to be there.

It means that the only way to "test you functions in general" would be to replicate WordPress behavior (e.g. logic applied on priority, accepted args number, what happen when hooks added while executing the same hook, or after the hook is excuted...) something that I really don't want to do because:

  • not doing that exactly how WordPress does it would be very bad, giving confidence on some code that might have passing tests, but might break once in production
  • doing exactly how WordPress does it adds a big maintanance burden because it needs to take into account different versions of WordPress and check at every release that nothing changed

So, IMO, if to "test you functions in general" you need to rely on WordPress, your best option is probably require WordPress functions. Luckily plugins.php, that contains declaration of hook functions, in recent WP versions, can be required in isolation (see https://github.com/WordPress/WordPress/blob/master/wp-includes/plugin.php#L17).

Alternatively, it would be cool if Brain Monkey could somehow be extended to provide this functionality...

If you manage to have a HookExpectationExecutor that might work as you like, I can look into a way to make Brain Monkey internal objects overridable, e.g. accepting PSR-11 container in Container and looking there before instantiating default objects.

Note that to make it work you'll might need to also override the Brain Monkey version of WP hook functions, but that is already doable, in fact Brain Monkey checks if functions are exists before to declare them (see https://github.com/Brain-WP/BrainMonkey/blob/master/inc/wp-hook-functions.php) so if you declare your version of any of those functions before that file is required it'll work.

@gmazzap
Copy link
Contributor

gmazzap commented Jul 22, 2019

By the way, in v2.3.0 I improved the documentation to be more clear.

More specifically here: https://github.com/Brain-WP/BrainMonkey/blob/master/docs/wordpress-tools.md

It is now said:

[...]
This works as long as there's no code that actually adds filters to "the_title" hook, so we expect that the title stay unchanged. And that's what happen.

If in the code under test there's something that adds filters (i.e. calls add_filter), the Brain Monkey version of apply_filters will still return the value unchanged, but will allow to test that apply_filters has been called, how many times, with which callbacks and arguments are used.

So I guess I can close this issue. Of course, feel free to open again if you think there's anything more things to be said here, but regarding extending Brain Monkey, if you have something, it is better to open a new issue.

Thanks.

@gmazzap gmazzap closed this as completed Jul 22, 2019
@XedinUnknown
Copy link
Author

XedinUnknown commented Jul 22, 2019

That should solve the confusion, thank you :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants