10up / wp_mock Goto Github PK
View Code? Open in Web Editor NEWWordPress API Mocking Framework
Home Page: https://wp-mock.gitbook.io
License: Other
WordPress API Mocking Framework
Home Page: https://wp-mock.gitbook.io
License: Other
The unit tests we currently have should be behavioral tests and some of the behavioral tests we have should be unit tests.
The following helper methods need tests:
echoFunction
alias
I might be a little confused here, but when I run \WP_Mock::expectActionAdded, I get an all OK from the test, but the expectActionAdded calls don't seem to count towards the assertion count. Am I doing something wrong or does that mean they're passing and they would return a PHP error if they weren't?
\WP_Mock::expectActionAdded( 'admin_bar_menu', array( $this->Site_Lockdown, 'page_lockdown_display' ), 500 );
\WP_Mock::expectActionAdded( 'admin_init', array( $this->Site_Lockdown, 'set_lockdown_option' ), 105 );
\WP_Mock::expectActionAdded( 'init', array( $this->Site_Lockdown, 'lockdown_this_thing' ), 105 );
\WP_Mock::expectActionAdded( 'init', array( $this->Site_Lockdown, 'lockdown_actions' ), 5 );
\WP_Mock::expectActionAdded( 'admin_init', array( $this->Site_Lockdown, 'lockdown_actions' ), 5 );
$this->Site_Lockdown->hooks();
I wanted to start a discussion around this idea. The last release on the 3.x branch was 6 months ago. I'm not sure if the branch has received its EOL yet. Will investigate.
Anyway, 4.0 introduced a lot of nice things. I'm primarily interested in the addition of the concept of "risky" tests for #18.
Love WP_Mock so far, it's a great tool that is helping me write better WordPress code and have better confidence in it. While I was testing a plugin I encountered the following problem.
Given a class under test like this:
<?php
class Example {
public function sample($input) {
//do stuff
$sanitized = sanitize_text($input);
//do more stuff
}
public function other($input) {
//do stuff
$sanitized = sanitize_text($input);
//do more stuff
}
}
?>
In order to test sample
and other
we need to mock sanitize_text()
for which we can use the method \WP_Mock::wpPassthruFunction('sanitize_text')
to check that each method calls it at least once:
<?php
class MyTestClass extends PHPUnit_Framework_TestCase {
public function setUp() {
\WP_Mock::setUp();
}
public function tearDown() {
\WP_Mock::tearDown();
}
public function testSampleExpectsSanitizeTextCalledOnce() {
// arrange
\WP_Mock::wpPassthruFunction('sanitize_text', array('times' => 1));
$input = 'Foo';
$Class = new \Example();
// act
$Class->sample($input);
}
public function testOtherExpectsSanitizeTextCalledOnce() {
// arrange
\WP_Mock::wpPassthruFunction('sanitize_text', array('times' => 1));
$input = 'Foo';
$Class = new \Example();
// act
$Class->other($input);
}
}
?>
When we run phpunit, the first test passes and the second test throws an error flagging a call to undefined function Patchwork\replace(). phpunit output:
.PHP Fatal error: Call to undefined function Patchwork\replace() in .../vendor/10up/wp_mock/WP_Mock/Functions.php on line 216
Fatal error: Call to undefined function Patchwork\replace() in .../vendor/10up/wp_mock/WP_Mock/Functions.php on line 216
The solution: add \WP_Mock\Handler::cleanup()
to tearDown();
:
<?php
class MyTestClass extends PHPUnit_Framework_TestCase {
public function setUp() {
\WP_Mock::setUp();
}
public function tearDown() {
\WP_Mock::tearDown();
\WP_Mock\Handler::cleanup();
}
Hey folks. Thank you for this great framework!
I'm having an issue with the times
argument to WP_Mock::wpFunction()
. Seems like a pretty basic use-case so I'm not totally certain I didn't miss something. Here's the minimal repro code:
<?php
require_once 'vendor/autoload.php';
use PHPUnit\Framework\TestCase;
trait HasCustomAdminFilters {
public function add_admin_filter( $name, $options, $postType, callable $queryModifier ) {
add_action('pre_get_posts', function(\WP_Query $query) use($name, $postType, $queryModifier) {
// do stuff
});
}
}
class HasCustomAdminFiltersTest extends TestCase {
public function setUp() {
WP_Mock::setUp();
}
public function tearDown() {
WP_Mock::tearDown();
}
public function testAddAdminFilter() {
WP_Mock::wpFunction('add_action', [
'times' => 1,
]);
// register the action handlers
$this->getObjectForTrait('HasCustomAdminFilters')->add_admin_filter(
'custom_filter',
['dropdown' => 'options'],
'my_post_type',
function() {}
);
}
}
Running this test outputs this error:
Mockery\Exception\InvalidCountException: Method add_action() from Mockery_0__wp_api should be called
exactly 1 times but called 0 times.
/vagrant/projects/groot/vendor/mockery/mockery/library/Mockery/CountValidator/Exact.php:37
/vagrant/projects/groot/vendor/mockery/mockery/library/Mockery/Expectation.php:297
/vagrant/projects/groot/vendor/mockery/mockery/library/Mockery/ExpectationDirector.php:120
/vagrant/projects/groot/vendor/mockery/mockery/library/Mockery/Container.php:297
/vagrant/projects/groot/vendor/mockery/mockery/library/Mockery/Container.php:282
/vagrant/projects/groot/vendor/mockery/mockery/library/Mockery.php:142
/vagrant/projects/groot/vendor/10up/wp_mock/WP_Mock.php:112
/vagrant/projects/groot/test/HasCustomAdminFiltersTest.php:30
I'm using WP_Mock at dev-master
(updated just now) and PHPUnit 5.5.4 on PHP 5.6.
Any ideas?
Using the example provided in the readme you'll receive a fatal error.
$ phpunit --bootstrap tests/bootstrap.php tests/MyTestClass.php
PHPUnit 3.7.31 by Sebastian Bergmann.
PHP Fatal error: Call to undefined function setup_postdata()
When executing the first test of Using WP_Mock, with its respective function my_permalink_function. And also other test, WP_Mock always returns false.
I'm trying to install wp_mock with composer, but it's telling me that there's a conflict with phpunit 6.
> /Applications/MAMP/bin/php/php7.0.0/bin/php /usr/local/bin/composer require --dev 10up/wp_mock:dev-dev
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Your requirements could not be resolved to an installable set of packages.
Problem 1
- 10up/wp_mock dev-dev conflicts with phpunit/phpunit[6.1.x-dev].
- 10up/wp_mock dev-dev conflicts with phpunit/phpunit[6.1.x-dev].
- 10up/wp_mock dev-dev conflicts with phpunit/phpunit[6.1.x-dev].
- 10up/wp_mock dev-dev conflicts with phpunit/phpunit[6.1.x-dev].
- Installation request for 10up/wp_mock dev-dev -> satisfiable by 10up/wp_mock[dev-dev].
- Installation request for phpunit/phpunit (locked at 6.1.x-dev) -> satisfiable by phpunit/phpunit[6.1.x-dev].
Installation failed, reverting ./composer.json to its original content.
Process finished with exit code 2 at 14:03:04.
Execution time: 4,563 ms.
Any advice welcome on this...
The whole package is WP-branded; we don't need no stinkin' redundancy don't need no stinkin' redundancy.
TL;DR: when I call add_action()
with a closure callback, expectHookAdded()
does not recognize the closure as having been called. Adding a call to $responder->react()
inside expectHookAdded()
fixes this.
Since the real add_action()
is designed to take any callable, I would expect to be able to call expectHookAdded
with any valid callable, including a closure.
For example, I am trying to unit test a trait that encapsulates adding an admin filter on the Posts listing screen. Specifically, I'm testing the trait's addAdminFilter()
method. I would expect to be able to do something like this:
<?php
require_once 'vendor/autoload.php';
use PHPUnit\Framework\TestCase;
trait HasCustomAdminFilters {
public function addAdminFilter( $name, $options, $postType, callable $queryModifier ) {
// allow user to define how they want the admin query modified
add_action('pre_get_posts', function(\WP_Query $query) use($name, $postType, $queryModifier) {
// call the user-defined query modifying code
$queryModifier($query);
});
}
}
class HasCustomAdminFiltersTest extends TestCase {
public function setUp() {
WP_Mock::setUp();
}
public function tearDown() {
WP_Mock::tearDown();
}
public function testAddAdminFilter() {
$callback = function() {};
WP_Mock::expectActionAdded('pre_get_posts', WP_Mock\Functions::type('callable'));
// register the action handlers
$this->getObjectForTrait('HasCustomAdminFilters')->addAdminFilter(
'custom_filter',
['dropdown' => 'options'],
'my_post_type',
$callback
);
}
}
This throws:
Mockery\Exception\InvalidCountException: Method intercepted() from Mockery_0__intercept should be called at least 1 times but called 0 times.
Digging into the WP_Mock
class, I see that the expectHookAdded()
code creates an internal mock object and tells it to be intercepted
; then it "performs" the added hook.
This works for the basic use-case of declaring a global function, e.g. foo()
, and then calling add_action('pre_get_posts', 'foo')
. But in my use-case, I want to encapsulate my callback as a closure, not expose it as a global function. Adding a call to $responder->react()
at the end of expectHookAdded()
fixes this issue.
I'm not sure if such a change would have other implications. Is it an appropriate solution? Should expectAction()
also get the same treatment?
The WP_Mock::expectAction
does not seem to work well or I misunderstood it. The setUp and tearDown methods use the WP_Mock::setUp()
and WP_Mock::tearDown()
as well. And then this test:
public function testItStoresPostBaseAsOption() {
$_POST['comfort_permalink_blog_base'] = uniqid();
\WP_Mock::expectAction( 'update_option' );
comfort_admin_options_permalink(); // within this an update_option is done
unset( $_POST['comfort_permalink_blog_base'] );
}
Debugging the code I can say that the update_option action is triggered. WP_Mock throws this error:
1) PHPUnit\Comfort\Admin\Options::testItStoresPostBaseAsOption
Mockery\Exception\InvalidCountException: Method intercepted() from Mockery_0__intercept should be called
at least 1 times but called 0 times.
Why does wp_mock tell me that when the action is triggered?
I am using version 0.1.1.
Okay so I dunno if I'm just missing something here. I've trimmed everything down to the very very basics. I have WP_Mock setup, and I have a few working unit tests already. I wanted to check if an action was being done do_action
in a function although with anything I do, I get this error after the test:
Mockery\Exception\InvalidCountException: Method intercepted() from Mockery_0__intercept should be called
at least 1 times but called 0 times.
/vagrant/www/wordpress-default/wp-content/plugins/sumo-search/vendor/mockery/mockery/library/Mockery/CountValidator/AtLeast.php:48
/vagrant/www/wordpress-default/wp-content/plugins/sumo-search/vendor/mockery/mockery/library/Mockery/Expectation.php:271
/vagrant/www/wordpress-default/wp-content/plugins/sumo-search/vendor/mockery/mockery/library/Mockery/ExpectationDirector.php:120
/vagrant/www/wordpress-default/wp-content/plugins/sumo-search/vendor/mockery/mockery/library/Mockery/Container.php:297
/vagrant/www/wordpress-default/wp-content/plugins/sumo-search/vendor/mockery/mockery/library/Mockery/Container.php:282
/vagrant/www/wordpress-default/wp-content/plugins/sumo-search/vendor/mockery/mockery/library/Mockery.php:142
/vagrant/www/wordpress-default/wp-content/plugins/sumo-search/vendor/10up/wp_mock/WP_Mock.php:112
/vagrant/www/wordpress-default/wp-content/plugins/sumo-search/tests/test-titan-framework-checker.php:12
This is my back to basics test file:
<?php
class TitanFrameworkCheckerTest extends WP_UnitTestCase {
public function setUp() {
parent::setUp();
\WP_Mock::setUp();
}
public function tearDown() {
parent::tearDown();
\WP_Mock::tearDown();
}
function test_perform_check() {
$o = new TitanFrameworkChecker();
\WP_Mock::expectAction( 'my_action' );
$result = $o->perform_check();
}
}
Now this is the code being checked:
class TitanFrameworkChecker {
public function perform_check() {
do_action( 'my_action' );
}
}
That test code above gives out the same error for me. I can't get this simple one to work. I've also tried with other functions like expectFilterAdded
.
I would appreciate any help on this.
Only a minor cosmetic change in the installation instructions: this package is clearly a dependency only required during development at should be added as such. Who wants to run unit tests on a production environment? :)
The required switch for this is --dev
(https://getcomposer.org/doc/03-cli.md#require):
composer require --dev 10up/wp_mock:dev-master
I'm having difficulty with WP_Mock when I'm disabling mocking nonexistent methods:
Mockery has a setting that prevents you creating imaginary tests which mock methods which don't exist:
\Mockery::getConfiguration()->allowMockingNonExistentMethods(false);
When I enable this and use e.g.
\WP_Mock::wpFunction('get_posts', ...
Mockery complains because the WP methods I'm mocking don't exist on the objects in question (presumably because they're global functions). Here's the error:
Mockery's configuration currently forbids mocking the method get_posts as it does not exist on the class or object being mocked
I'm working around this by selectively disabling this setting in tests which use WP_Mock (I'm using peridot for tests):
beforeEach(function() {
\WP_Mock::setUp();
\Mockery::getConfiguration()->allowMockingNonExistentMethods(true);
});
afterEach(function() {
\WP_Mock::tearDown();
\Mockery::getConfiguration()->allowMockingNonExistentMethods(false);
});
Does anyone know of a better approach? Or is the solution to patch WP_Mock such that mockery ignores these methods when checking for mocking nonexistent methods?
Things like the tests for the various flags for userFunction
work as expected, etc.
How use expectActionAdded
and expectFilterAdded
with class method. Example
class SupportWebP
{
public function __construct()
{
add_filter('upload_mimes', [$this, 'supportWebP']);
}
public function supportWebP($extensions)
{
$extensions['webp'] = 'image/webp';
return $extensions;
}
}
How a can test upload_mimes
filter?
Currently you can't declare expectations on things like type or fuzzy matching for arguments. If the args don't match exactly, we can't match an action or filter. I'd like to be able to do something like
WP_Mock::expectActionAdded( 'some_action', array( WP_Mock::typeMatcher( 'SomeClass' ), 'method' ) );
new SomeClass(); // Action added in constructor
Hey guys, googled quite a lot but haven't found anything that worked for me, so I came directly to you. It's about the setup.
All I want to do is to test my own functions that are in functions.php
.
I have to setup a phpunit.xml and also a bootstrap file so I can automate the tests.
My bootstrap file:
require_once dirname( __FILE__ ) . '/../../../../vendor/autoload.php';
require_once dirname( __FILE__ ) . '/../functions.php';
And my phpunit.xml calls it.
The issue:
Before using bootstrap.php and phpunit.xml my test was working fine, but with all that require
and stuff.
After this setup, now I'm getting:
Fatal error: Call to undefined function add_action()
Tried a few things already, like \WP_Mock::expectAction
but not successfully. Any advice?
I think my setup is not complicated so it shouldn't be that hard.
Thank you!
We are not doing well in this regard...
I realize this may not be in line with the WP_Mock mission, but I've seen several situations where it's helpful to use a combination of using WP_Mock::wpFunction()
and Mockery::mock()
together. In these cases it's awkward (and a little confusing for other developers) to switch between the WP_Mock and Mockery expectation syntaxes.
To be more specific, I might write the following in a test:
\WP_Mock::wpFunction( 'get_permalink', array( 'args' => 42, 'return' => 'http://example.com/foo' ) );
\Mockery::mock( 'MyClass' )->shouldReceive( 'getLink' )->with( 42 )->andReturn( 'http://example.com/foo' );
It seems relatively easy to allow wpFunction()
to return the Mockery\Mock
object (by returning $expectation
from Functions::set_up_mock
through Functions::register
). If that were done, it would be up to the user to choose which style of syntax to use, which while it is inconsistent, provides more flexibility.
Thus, I could re-write the above as:
\WP_Mock::wpFunction( 'get_permalink' )->with( 42 )->andReturn( 'http://example.com/foo' );
\Mockery::mock( 'MyClass' )->shouldReceive( 'getLink' )->with( 42 )->andReturn( 'http://example.com/foo' );
Consider this a feature request. I'd be happy to write the PR to go with it. ๐
Maybe I'm using this wrong but if I require this library in my plugin file, I can use it for testing, but it also conflicts with real wordpress functions. Is there a way to autoload this for testing only and still use composer?
The repository defaults to showing dev
branch and it looks like that is the branch with active development. master
hasn't been updated in a few years. Which branch do you recommend we install?
Sincerely I am very surprised to find this bug and found nothing on existing issues: I think this problem should be so common that seems impossible no one has reported it before.
I still have the doubt the problem it's.. me.
However, I tried to report things as better as possible and to give all the info to reproduce the bug.
When a method to be tested call do_action
passing some arguments, like
do_action( 'custom_hook', 'foo', 'bar' )
currently, is impossible for tests to add expectations using expectAction
, e .g.
\WP_Mock::expectAction( 'custom_hook' );
Because they fail.
Here, in this gist there is the I used, and can be used to reproduce the bug.
I've used just 2 files: a class to be tested an a test class, keeping things as simple as possible, but the problem remains. In a comment in the Gist there is also a screenshot.
I also added in the Gist the composer.json
I used and, really, those 3 are all the file I used (aside vendor folder, of course).
However, I think I solved the issue by editing a couple of lines in WP_Mock/Action.php
.
This compare shows the code I used. It seems to work, and to not affect anything else, but I don't know the repo so well, so I can miss something.
When I use WP_Mock::expectActionAdded()
in my tests it always throws an exception.
Code being tested:
Class se_admin {
var $version = '6.9';
function se_admin() {
// Load language file
$locale = get_locale();
if ( !empty($locale) )
load_textdomain('SearchEverything', SE_ABSPATH .'lang/se-'.$locale.'.mo');
add_action('admin_head', array(&$this, 'se_options_style'));
add_action('admin_menu', array(&$this, 'se_add_options_panel'));
}
}
Test:
<?php
class Admin_Tests extends PHPUnit_Framework_TestCase {
public $seadmin;
function setUp() {
WP_Mock::setUp();
}
function tearDown() {
WP_Mock::tearDown();
}
function test_setup() {
WP_Mock::wpFunction( 'get_locale', array(
'times' => 1,
'return' => '',
));
WP_Mock::expectActionAdded( 'admin_head', array( &$this->seadmin, 'se_options_style' ) );
WP_Mock::expectActionAdded( 'admin_menu', array( &$this->seadmin, 'se_add_options_panel' ) );
$this->seadmin = new se_admin();
}
}
Results:
PHPUnit 3.7.29 by Sebastian Bergmann.
Configuration read from /Users/ninnypants/Sites/search-everything-wordpress-plugin/tests/phpunit.xml.dist
E
Time: 47 ms, Memory: 3.75Mb
There was 1 error:
1) Admin_Tests::test_setup
Mockery\Exception\InvalidCountException: Method intercepted() from intercept should be called
at least 1 times but called 0 times.
/Users/ninnypants/Sites/search-everything-wordpress-plugin/vendor/mockery/mockery/library/Mockery/CountValidator/AtLeast.php:47
/Users/ninnypants/Sites/search-everything-wordpress-plugin/vendor/mockery/mockery/library/Mockery/Expectation.php:236
/Users/ninnypants/Sites/search-everything-wordpress-plugin/vendor/mockery/mockery/library/Mockery/ExpectationDirector.php:120
/Users/ninnypants/Sites/search-everything-wordpress-plugin/vendor/mockery/mockery/library/Mockery/Container.php:203
/Users/ninnypants/Sites/search-everything-wordpress-plugin/vendor/mockery/mockery/library/Mockery/Container.php:188
/Users/ninnypants/Sites/search-everything-wordpress-plugin/vendor/mockery/mockery/library/Mockery.php:85
/Users/ninnypants/Sites/search-everything-wordpress-plugin/vendor/10up/wp_mock/WP_Mock.php:81
/Users/ninnypants/Sites/search-everything-wordpress-plugin/tests/admin-tests.php:11
FAILURES!
Tests: 1, Assertions: 0, Errors: 1.
When dealing with code inspection, something like this cause some warning:
\WP_Mock::onFilter( 'my_filter' )->with( false )->reply( $my_reply );
The problem is that the returned type of the with
method is unclear.
Apparently, responders does not implement any interface, so I guess that the only way to solve this is by adding this PHPDoc to the \WP_Mock\Hook::with
method:
/** @return Action_Responder|Filter_Responder */
public function with() {
$args = func_get_args();
$responder = $this->new_responder();
if ( $args === array( null ) ) {
$this->processors['argsnull'] = $responder;
} else {
$num_args = count( $args );
$processors = &$this->processors;
for( $i = 0; $i < $num_args - 1; $i++ ) {
$arg = $this->safe_offset( $args[ $i ] );
if ( ! isset( $processors[ $arg ] ) ) {
$processors[ $arg ] = array();
}
$processors = &$processors[ $arg ];
}
$processors[ $this->safe_offset( $args[ $num_args - 1 ] ) ] = $responder;
}
return $responder;
}
Could this be possible, so we can reduce the number of warnings from our IDEs?
This will be tracked in the feature/behat-tests branch
WP_Mock is an API mocking framework, built and maintained by 10up for the purpose of making it possible to properly unit test within WordPress.
Sorry to bother you. But I've been doing some research while building out unit tests for a WordPress plugin I'm building and discovered a misnomer in the vernacular being used in the community with regard to what actually constitutes unit testing
.
My understand is that, in using WP_Mock
or similar, while creating WordPress plugins what was is doing is actually creating an "integration test" and not a "unit test".
To help justify my rationale I've set-up an application capable of both unit tests and integration testing. Here's what an example integration test looks like:
/**
* Sample test case.
*/
class SampleTest extends WP_UnitTestCase {
/**
* A single example test.
*/
function test_sample() {
// Replace this with some actual testing code.
$this->assertTrue( true );
}
}
It's an integration test because it requires an understanding of and dependence on WordPress internals in order to execute.
Whereas here's what an actual unit test looks like:
namespace hyperdrive\spec;
use function hyperdrive\enter_hyperspace;
describe('hyperdrive', function () {
describe('enter_hyperspace()', function () {
it('echos empty script given empty string', function () {
$closure = function () { enter_hyperspace(''); };
expect($closure)->toEcho('<script></script>');
});
it('echos string in script given string', function () {
$closure = function () { enter_hyperspace('Danger, Will Robinson!'); };
expect($closure)->toEcho('<script>Danger, Will Robinson!</script>');
});
it('does not echo unexpected string', function () {
$closure = function () { enter_hyperspace('Danger, Will Robinson!'); };
expect($closure)->not->toEcho('<script>Warning! Warning!</script>');
});
});
});
Above snippet copied from hyperdrive.spec.php
and licensed for reuse under AGPL.
Notice there is no reliance on WordPress in order to operate; code is operating on PHP source capable of being run in any system and independent WordPress internals.
Which begs the question, what is actually considered a unit test when working with WordPress, and should the community consider adjusting vernacular to help prevent misunderstanding and starting with adjusting the blockquote pulled from the WP_Mock
README file copied above?
I'm quite sure that, some time ago, I had found some answer to this question here or somewhere else, but I could not find it anymore and I don't remember if I had found a solution.
What I need is writing a test and expect a given action to be never called: how can I do that?
It looks like \WP_Mock::expectAction
has no option where I can specify that.
Any tip?
I use the singleton pattern for most of the classes in my plugin.
trait Singleton {
private static $instances = array();
/**
* Provides access to a single instance of a module using the singleton pattern
*
* @return object
*/
public static function get_instance() {
$module = get_called_class();
if ( ! isset( self::$instances[ $module ] ) ) {
self::$instances[ $module ] = new $module();
}
return self::$instances[ $module ];
}
}
The classes using this trait also have a register_hook_callbacks
method that's called from the constructor. All of the singletons are instantiated in this fashion:
add_action( 'plugins_loaded', array( Foo::class, 'get_instance' ) );
As soon as plugins are loaded, the instance is set up, the constructor is called, and the hooks are registered. I can't seem to get this to play nice with WP_Mock
. Here's what I thought would work:
/** @test */
public function it_adds_filters()
{
$instance = Cron::get_instance();
WP_Mock::expectActionAdded('wp', [$instance, 'maybe_schedule_cleanup']);
}
I know the hook is registered, but the test fails. I've also tried explicitly calling the hook registration after setting the expectation.
/** @test */
public function it_adds_filters()
{
$instance = Cron::get_instance();
WP_Mock::expectActionAdded('wp', [$instance, 'maybe_schedule_cleanup']);
$instance->register_hook_callbacks();
}
Any suggestions?
Thanks.
This is related to #84. We will eventually need to support PHPUnit 6. For now, we're just requiring a version >=4.4 and <6.0. Ideally we can support PHPUnit 6.0 and lower, although I'm not sure how, since a lot of the codebase has been restructured (e.g. PHPUnit_Framework_TestCase
has been renamed to PHPUnit\Framework\TestCase
).
In anticipation to writing tests for our test framework, I'd like to propose that we move our code into a src
directory. Obviously we'd need to update the autoload
configuration in composer.json
. Any other unintended consequences anybody can think of?
I get a PHP Fatal error PHP Fatal error: Call to undefined function BBL\Classes\__()
when running my tests. I've tried mocking WordPress's __()
by doing
\WP_Mock::wpPassthruFunction('__',);
and
\WP_Mock::wpFunction( '__', [
'times' => 1,
'args' => ['%s is not a valid app-key for %s shortcode', 'bbl'],
'return_args' => 0
] );
I get the error both times.
Is the __() function a function I'll need to mock? I just want the first argument returned.
This is probably not possible to fix this because of how PHP works, but I wanted to mention this issue because it can have some side effects in the sequence of tests.
How to reproduce the issue:
Add a function mock in the first test class to be run:
\WP_Mock::wpFunction( 'my_custom_function', array( 'return' => true ) );
In another test class (coming after the first one), call this function:
$test = my_custom_function();
=> $test
will be set to null
.
Perhaps it deserves one note in the documentation (https://github.com/10up/wp_mock).
Thanks for the great framework!
After #17 has been completed, we need to deprecate the WP-branded method names. They'll still work, but they should be avoided and replaced in test suites.
I'd really like to have the methods create some sort of warning for PHPUnit that would alert the user to the fact that they're using a deprecated method and need to update their tests.
After WP_Mock is loaded, whenever do_action
is called, a PHP error is thrown. For example, with this test in my-Test.php
:
<?php
function my_wp_function() {
do_action( 'foo', 'bar' );
}
class MyTest extends PHPUnit_Framework_TestCase
{
public function setUp() {
\WP_Mock::setUp();
}
public function tearDown() {
\WP_Mock::tearDown();
}
public function testRunsCorrectly()
{
my_wp_function();
$this->assertEquals(1, 1);
}
}
If I then run phpunit --bootstrap vendor/autoload.php my-Test.php
, I get the following error:
PHPUnit 4.5.0 by Sebastian Bergmann and contributors.
E
Time: 31 ms, Memory: 3.00Mb
There was 1 error:
1) MyTest::testRunsCorrectly
Undefined index: bar
/Users/payton/tmp/wp_mock_test/vendor/10up/wp_mock/WP_Mock/Action.php:37
/Users/payton/tmp/wp_mock_test/vendor/10up/wp_mock/WP_Mock/API/function-mocks.php:35
/Users/payton/tmp/wp_mock_test/my-Test.php:4
/Users/payton/tmp/wp_mock_test/my-Test.php:19
The undefined index appears to be whatever the second argument to do_action
is.
I've used WP_Mock many times before with no issues, which makes me think I'm doing something foolish here, but I can't figure out what. Please point it out to me if I'm making an obvious mistake.
Here's my composer installed packages:
installed:
10up/wp_mock dev-master 8f2e98a A mocking library to take the pain out of unit testing for WordPress
antecedent/patchwork 1.3.4 A pure PHP library that lets you redefine user-defined functions at runtime.
hamcrest/hamcrest-php v1.2.2 This is the PHP port of Hamcrest Matchers
mockery/mockery 0.9.4 Mockery is a simple yet flexible PHP mock object framework for use in unit testing with PHPUnit, PHPSpec or any other testing framew...
(I created a gist of the above failing test)
It seems to me that at most, there could be warnings or notices generated for things not properly mocked up for passing thru or mocking things like esc_attr / esc_html / etc.
It also seems pretty difficult to rectify these when running tests results in fatals where a function doesn't exist in the namespace, but it's a global WP function.
Should there be a way to enable tests to run as expected, with notices thrown out for things that are not covered?
I know that wp_mock and WordPress core Unit tests work differently and it may not even be possible to have them run together.
I use both and like both. But there are certain tests for which I would prefer wp_mock and certain tests for which I would prefer WordPress core Unit tests. But unfortunately right now there is no way to configure both of them for a single plugin. You have to pick one and stick with it even if you can group all the 'mock' tests using a separate testsuite or phpunit group and invoke the separately from phpunit.
What I am suggesting is to have a way to setup both of them together for a plugin and invoke the appropriate framework and their tests using either phpunit's group or testsuite feature.
I know that there was some recent discussion about it and I am interested in knowing everyone's thoughts on it.
When a function is mocked using Functions::create_function()
, two things are created:
eval()
Handler:register_handler()
When the test is complete and Functions::flush()
is called, it removes the
handler, but nothing removes the function itself, which will continue to call
Handler::handle_function()
in later tests, except that this will now be a
noop.
This can create problems when, for example, one test mocks a function to allow a
certain series of function calls, and then a later test also uses that series of
function calls, but does not mock the function. In this case, if the first test
is run before the second test, they will both pass, but if the second test is
run first (or in isolation), it will fail because it calls an undefined
function. Test Mocks should not cause tests to fail if run in different order.
While it may be impossible to replicate a call to an undefined function, we can
simulate it by throwing an Exception. I'd suggest a change that causes
Handler::handle_function()
to throw an Exception if it doesn't find a matching
handler.
Example:
<?php
class SomeClass {
public function do_something( $times ) {
$user = wp_get_current_user();
//...
return true;
}
}
class MyTestClass extends PHPUnit_Framework_TestCase {
public function setUp() {
\WP_Mock::setUp();
$this->something = new SomeClass();
}
public function tearDown() {
\WP_Mock::tearDown();
}
public function test_do_something() {
\WP_Mock::wpFunction( 'wp_get_current_user' );
$this->assertTrue( $this->something->do_something( 1 ) );
}
public function test_do_something_again() {
$this->assertTrue( $this->something->do_something( 2 ) ); // This should cause a fatal error, but it does not if run after the previous test
}
}
Using the latest PHPUnit WP_Mock causes a failure with the following error:
PHP Fatal error: Class 'PHPUnit_Framework_TestCase' not found in .../vendor/10up/wp_mock/WP_Mock/Tools/TestCase.php on line 12
https://blog.codinghorror.com/pick-a-license-any-license/
Because I did not explicitly indicate a license, I declared an implicit copyright without explaining how others could use my code. Since the code is unlicensed, I could theoretically assert copyright at any time and demand that people stop using my code. Experienced developers won't touch unlicensed code because they have no legal right to use it.
I was able to track this down to the dependency of antecedent/patchwork v1.4.1. They recently updated, and in the process removed their autoloader from composer. This appears to be causing some issues with the require statement in WP_Mock which includes Patchwork within the bootstrap function.
watson$ ./vendor/bin/phpunit
Warning: Please import Patchwork from a point in your code where no user-defined function, class or trait is yet defined. composerrequire1d885e985f1407f27ab4585581d59bad() and possibly others currently violate this. in /Users/user/Sites/vagrant-local/www/project/htdocs/wp-content/themes/theme-name/vendor/antecedent/patchwork/Patchwork.php on line 93
PHPUnit 4.8.23 by Sebastian Bergmann and contributors......
Time: 545 ms, Memory: 12.50Mb
OK (5 tests, 8 assertions)
This will be off by default.
Currently, if a function is defined by WP_Mock and has no expectations defined, it will run as a no-op function with a return value of null
. We need a strict mode that will cause failure if a function is used without an explicit expectation. This will also cause add_action
and add_filter
to fail without an explicit expectation (currently the do not fail if unexpected).
Right now I'm working on a project where do_action()
gets triggered only when the rest of the function succeeds. It'd be great to be able to say "in this method where I'm testing how it fails the action is not called.
Edit: obviously the method name could use some work :)
e.g. WP_Mock::wpFunction()
would be renamed to WP_Mock::function()
. Of course, for backwards compatibility, the original names would remain and would simply call the new names internally.
I believe this relates to #56 as well. I cannot install this package with the latest version of lucatume/wp-browser because of a dependancy mis-match with antecedent/patchwork
wp_mock uses antecedent/patchwork version 1.x
wp-browser now uses antecedent/patchwork version 2.x
Any plans to update the antecedent/patchwork requirements of this project?
Something like:
WP_Mock::alias( 'wp_hash', 'md5' );
WP_Mock::alias( 'trailingslashit', function( $it ) { return rtrim( $it, '/' ) . '/'; } );
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.