1

I have a fairly crusty PHP app that I'm refactoring and adding phpunit tests for as I go. A common situation in the application is user warning messages for incorrect configuration (the application takes in a configuration file and produces images and HTML as output).

So I have a bunch of code sprinkled through the application that's like:

wm_warn("You can't make a contrast with 'none' - guessing black. [WMWARN43]\n");

where wm_warn writes to either stderr or a logfile depending on context.

How can I test these warnings with phpunit?

Importantly - this isn't a fatal error, so it's not possible (AFAIK) to replace it with an exception and use phpunit's "expected exception" feature.

Is this a use case for a wm_warn mock? Or capturing stderr? Or using a different logging method to make the testing easier (e.g. log4php)?

2 Answers 2

1

Mocking might be the right solution, the PHPUnit manual describes mocking as such:

The practice of replacing an object with a test double that verifies expectations, for instance asserting that a method has been called, is referred to as mocking.

So you could observe that wm_warn was called, but it is not executed (nothing goes to the log).

You can read more about Simple Mocking under the PHPUnit Test Doubles topic that shows an example of this (where the above quote is also discussed). One problem you might face is that you can mock objects but not straight functions - so depends on the crustiness of your app :)

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks, nice writeup! The current log function is an actual function, but I can have that call a method on a logging object easily (with a view to folding it all up into a direct call to the logging object later).
1

Mocking (or rather a spy) would be the way forward.. it might be difficult to implement, depending on the application (you said it was fairly crusty)

How is the function loaded? If it's possible to intercept the call that includes the relevant file that contains the wp_warn function you can then change it instead to use your own mocked version which could then record the parameters it was called with (i.e. the error) into a (urgh, shudder) global variable that is accessible to the test case:

//...
if (defined('TEST_ENVIRONMENT')) {
    require_once 'wp_warn-test.php';
} else {
    require_once 'wp_warn.php';
}
//...

in wp_warn-test.php:

function wp_warn($error) 
{
    $_GLOBALS['SPY_WP_WARN_ERRORS'][] = $error;
}

and in the test:

$this->assertContains("You can't make a contrast with 'none' - guessing black. [WMWARN43]\n", $_GLOBALS['SPY_WP_WARN_ERRORS']);

It is very ugly, especially with the use of the global variable, however if it means that you managed to get a test in, it leaves you more opportunities in the future to refactor it and find more elegant solutions.

1 Comment

wm_warn can be in a separate file (it isn't at the moment). I think I'll go with it being a wrapper that calls methods in a global logging object, that is then mockable and replaceable. There are a couple of other loglevels (debug, trace) that should be handled in a single place too. I was already thinking about have some kind of LogObjectFactory so that in non-debug mode, the debug() calls are as short as possible, with a different log object having stub functions.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.