Let’s just say it up front: unit testing with WordPress is a royal pain in the ass.
To be fair, there are reasons. WordPress got started before PHP supported much in the way of namespacing and object oriented code. So, the difficulties stem from a legacy of far too many global functions and variables. The hyper-vigilance of the project about backward compatibility has meant that refactoring all these into proper classes and objects that can be mocked hasn’t happened, and likely won’t happen. Maybe ever. And I get the reasons. I really do. I’d go so far as to say that attention to backwards compatibility is a fine tradition.
Others have noted that unit testing plugins and themes is more akin to integration testing than unit testing. Fine. But again, because it’s hard to get very far in any WP plugin or theme context without calling a WP global function somewhere, you’re still stuck with hard-to-do integration testing.
For example, I was working on a plugin last week where I needed to sanitize some data. Now, it’s easy enough to call WP’s
sanitize_text_field(), and the “WP way” encourages me to use native WP functions whenever possible. But since it’s a global function in the global namespace, it breaks my unit tests. If WP had a
Validator class I could mock that easily enough, and move on.
For a while there have been some work-arounds for this. Google “unit test WordPress” and you’ll find 10-Up’s WP_Mock framework. It looks great, but it’s not compatible with PHPUnit 6. So to use this I have to rewrite all my unit tests for an out-of-date test version.
Next, there’s the Function Spy package. So I can mock global functions. But for every WP function I want to mock there’s an awful lot overhead.
Then, I came across BrainMonkey. Like WP_Mock, this seems to do the trick. But when I run my tests with this, it increases the test run time by a factor of 100. I haven’t gone into it enough yet figure out why. My first guess is that it has to set up a similar amount of overhead as using Function Spy for every WP global function.
So, it seems that there really are not any good tools available. If I’m missing any, please (pretty please!) let me know.
There are other possibilities. I could redesign my code so that all validation is somehow deferred to a
ValidatorProxy class and restrict my calls to WP validation functions to calls from within that class. I can run my unit tests everywhere but on the
ValidatorProxy (and whatever other proxies I’ll need to set up to pinch-hit for WP globals) and worry about integration testing the proxies down the line.
As I wrote that last paragraph, it began to feel like I’d be re-writing WP_Mock. That’s a tall order. I can only guess that this is exactly how the folks at 10-Up started out. I’m also open to the possibility that I’m just naively
_doing_it_wrong() — in which case, again, someone please enlighten me!
I’ve been a huge WordPress fan for many years. I still like it. I still use it every day. WordPress Core itself takes testing and security seriously. Issues get fixed. But ask any WP plugin or theme author about their test coverage and you’ll likely get a blank stare. And it’s the themes and plugins where most WordPress site errors and security issues happen.
“Tests that are hard to write and run won’t get written or run.” That’s one of the the agile development mantras. Unfortunately, WordPress is a prime example of it’s truth. Lack of tests => lack of code confidence. If you ever find a bank’s website running on WordPress, run away.