Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How would you PHPUnit test a Wordpress function containing check_admin_referer()?

I've just start learning PHPUnit with Wordpress. I have a plugin that gets petition data from change.org. One of the admin class functions validates settings from a Wordpress admin area and also calls `check_admin_referer() as part of this validation.

  public function sc_validate_settings() {
    //check nonce field is valid
    check_admin_referer($this->plugin_name, 'security');    

    //get new settings 
    $settings = $this->sc_clean_new_settings();

    //validate url
    $valid_url = $this->sc_validate_url($settings['petition_url']);

    //validate api_key
    $valid_api_key = $this->sc_validate_api_key($settings['petition_api_key']);

    if ($valid_url && $valid_api_key) {

      $this->clean_settings = $settings;
      return true;

    }

    return false;

  }

This PHPUnit test passes if I comment out check_admin_referer() but I can't get it to pass if not.

  public function testValidateSettings() {    

    $this->assertTrue($this->plugin_admin->sc_validate_settings());

  }  

I've tried setting a nonce, action and _wp_http_referer manually and via wp_nonce_field() via $_POST in tests/bootstrap.php and in the test class itself. And I've read a bit about mock objects/methods but don't quite see how they might be used in this instance.

I'm probably completely misunderstanding how all this works but any help would be greatly appreciated!

like image 229
greenbricks Avatar asked Aug 12 '16 16:08

greenbricks


1 Answers

The issue is that tests run in command-line mode, and of course, the current user is not authenticated there.

There are a couple of alternatives how to make it work. One option is to stub either the auth-check function check_admin_referer() or its underlying wp_verify_nonce(). But it's not the best approach, as the tests there are of integration flavor, while stubbing is more an approach for unit tests.

A good solution is to authenticate user to make the tests pass. You can do it relatively easy like this:

public function testValidateSettings() {
    $_REQUEST['security'] = wp_create_nonce($this->plugin_admin->plugin_name);

    $this->assertTrue($this->plugin_admin->sc_validate_settings());
}

I'm not confident that $this->plugin_admin->plugin_name will work, as it might be a private property. So you can just pass it as a hardcoded string:

public function testValidateSettings() {
    $_REQUEST['security'] = wp_create_nonce('whatever your plugin name is');

    $this->assertTrue($this->plugin_admin->sc_validate_settings());
}

Also it would be great to cleanup after your test, so you should definitely have this in your test case:

public function tearDown() {
    unset($_REQUEST['security']);
}

I don't think you need to clean the newly created nonce after the tests, because the WP original tests do not clean that as well.

That's pretty it.


Improvement

If all your tests in the test case require authentication, you might want to put the nonce-creation into setUp() - this will make the test case prettier, as tear down code will match the setup one:

public function setUp() {
    $_REQUEST['security'] = wp_create_nonce('whatever your plugin name is');
}

public function tearDown() {
    unset($_REQUEST['security']);
}

public function testValidateSettings() {
    $this->assertTrue($this->plugin_admin->sc_validate_settings());
}

Suggestion

Install Xdebug and connect to it via your IDE - this will help you to go through the code step by step and see what's not working as you expected. This is better, than blindly struggling with all those nonces, http referrers, etc.


Note

The WP code is pretty sad. Huge list of functions in the global namespace, lack of OOP, no full cycle for request processing (e.g. unexpected die()) - is really frustrating, because it makes extension of the codebase and testing really hard.

So be prepared for more fights with the code :)

like image 192
Andrey Tserkus Avatar answered Nov 20 '22 09:11

Andrey Tserkus