Architecture and Unit Testing WordPress Code

In a few recent projects, I’ve been unit testing WordPress code and doing more general test-driven development than normal. I’m no stranger to TDD, but it’s not something I am religious about using. For me, it depends on the nature of the project.

There’s a lot of writing on TDD and its advantages, and a lot of people who use it swear by it. It builds a level of quality into a project and helps with adding new features, solving bugs, and modularizing code.

Unit Testing WordPress

Unit testing WordPress code is a bit of a mixed bag, and it comes down to the fact that object-oriented code in WordPress is often tightly coupled to both the business logic and the WordPress API.

Since TDD can help designing the architecture of a project, it can help guide how we can create more testable classes.

Unit Testing WordPress Code

When I was working on the WordPress Plugin Boilerplate, one of the classes that I included (and that’s still part of it) is the Loader class.

This class was responsible for registering various functions with hooks. Ultimately, it allowed for decoupling functionality specifically for tying functions into WordPress and functionality specifically for completing the business logic of a project.

Set up Unit Testing for WordPress
Using WP-CLI to help provide easy unit testing for WordPress.

And this is something that still contend is the way to go.

1. Business Logic

When writing code, business logic or domain logic refers to the part of the code that helps to solve the specific problem at hand.

These classes usually encapsulate ideas represented by the problem and the attributes and functions necessary to solve them. They will likely be (or should be) a collection of classes that communicate with one another and that break the solution into several, manageable parts.

Regarding WordPress, none of these functions will communicate with the hooks or anything like that. Sure, they may talk with several of the WordPress APIs, but they aren’t going actually to register any of their functions with WordPress.

Usually, that sits outside of solving a specific problem and creates unnecessary coupling between the solution and WordPress itself. Instead, you want to make sure that the code that binds your algorithms with WordPress are separate from the code that solves the problem.

2. Registering Code with WordPress

Registering code with WordPress does not mean that we ignore or forgo testing code that uses the WordPress API. It does, though, mean that we separate testing code that uses add_action or add_filter into a separate class (or a separate set of classes).

These particular calls tie your code to WordPress, and though it’s important to have functions tied to, say, certain filters to WordPress, we should be more concerned about what the function is doing rather than if it’s safely tied to WordPress.

To be clear, I’m not saying that we should never test if a function is separate from any of the WordPress hooks. Those functions should reside in a set of tests that are strictly designed to make sure the functionality is bound to the core application.

I do think, though, that it’s more important to test code specific to a problem domain rather than if a function is properly registered with a hook.

It’s easier to test manually that a function is tied to WordPress, and it’s less likely to change once it’s properly registered rather than code that is working on any input or code related to a specific problem.

A Layered Approach

If you’ve ever worked with .NET or Rails or another type of framework, then you’re likely familiar with an object-relational mapping system (or an ORM).

Simply put, this is a layer of software that allows us to work with information stored in the database in the application layer.

It maps rows and columns to classes so that we can literally work with the database in terms of objects. They also provide a level of data validation, sanitization, and other helpful functions.

One way of thinking about this is in terms of a stack. For example:

  • the database is at the lowest level,
  • the ORM sits on top of the database,
  • the application layer sits above that,
  • and the front-end is at the top.

You can make this is detailed and as nuanced as you’d like, though.

Though it does have features that help us work with the database, WordPress doesn’t have a specific layer of code like this. That doesn’t mean that we can’t introduce it in our projects, though.

The two points mentioned above can be thought of in similar fashion. Rather than thinking about it as an entire section such as an ORM, think of them as two separate layers in the application layer.

One layer is for talking directly with WordPress, and one layer is for handling the business logic that solves the overarching problem the project is trying to solve.

As mentioned at the beginning, this would allow us to test the business logic completely independently of if, say, a script is registered with WordPress.

And yes, there are times in which certain data structures will need to be tested (such as an array that represents the menu in the dashboard). There are ways to do this through mock classes, mock data, and other strategies, but that’s beyond the scope of this post (though I aim to talk about this more in future posts).

Ultimately, the point is that although WordPress isn’t as testable as some other programming platforms that are available, that doesn’t mean we can’t do our part to make our code more testable.

5 Replies to “Architecture and Unit Testing WordPress Code”

  1. Hey Tom,

    For testing WordPress projects, I recommend looking into BrainMonkey (https://brain-wp.github.io/BrainMonkey/). It allows you to unit test WP-specific code (like code you attach to actions) without actually loading the WordPress core at all.

    As for using a layered approach for your architecture, apart from the general concepts of DDD, I am a big fan of the hexagonal architecture (http://fideloper.com/hexagonal-architecture), and the basic principle of controlling the flow of your dependencies like described here: https://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html .

    Although WordPress does not fully fit into any of these as is, you can already make sure to only depend on WordPress on direct integration points.

    As an example, let’s say you want to access a REST API from within your WordPress backend and sync some content to your local WP database to reuse from there. I would then build a general PHP library (as a Composer package) for interacting with this API and returning the responses, an interface to persist this data in an AbstractDatabaseStore, and an interface to render an AbstractManagementScreen.

    Then, in a WordPress plugin, I would implement the AbstractDatabase as a WPDBStore and the AbstractManagementScreen as a WPAdminPageManagementScreen. This way, if I ever want to switch from WordPress to another CMS (let’s call it “Drupal”…), I just need to reimplement the above two interfaces as DrupalStore & DrupalManagementScreen and I’m good to go. My actual business logic does not need to change.

    This is just a simplified example to explain what I mean, so don’t judge me for any of the above!

    1. This is a really good comment, so I’ll try to take it point-by-point. A lot of what you said is more than good enough! 

      Some of it, I think, requires me to clarify some of what I’ve shared since I think I could’ve done a better job in the post.

      For testing WordPress projects, I recommend looking into BrainMonkey (https://brain-wp.github.io/BrainMonkey/). It allows you to unit test WP-specific code (like code you attach to actions) without actually loading the WordPress core at all.

      This is something I’m definitely going to look into. I’ve been on the fence about various ways to strictly test business logic outside of WordPress and I know PHPUnit is kind of the de-facto way of doing it, but when you’re also testing aspects of core it requires some heavy lifting from some of the tools mentioned in this post.

      As for using a layered approach for your architecture, apart from the general concepts of DDD, I am a big fan of the hexagonal architecture (http://fideloper.com/hexagonal-architecture), and the basic principle of controlling the flow of your dependencies like described here: https://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html.

      <3 DDD. I had a chance to take Eric Evan’s class years ago but that was when I was working with the enterprise. I still love many of the concepts but I’ve found it not always as easy to implement as some of his examples seemed to show (mainly because architecture in an existing code-base can be so much more complex or just different).

      Thanks for this post from Uncle Bob. He’s arguably my favorite software engineer author so I’m eager to read this.

      This is a really good comment, so I’ll try to take it point-by-point. A lot of what you said is definitely good enough! Some of it, I think, requires me to clarify some of what I’ve shared since I think I could’ve done a better job in the post.

      For testing WordPress projects, I recommend looking into BrainMonkey (https://brain-wp.github.io/BrainMonkey/). It allows you to unit test WP-specific code (like code you attach to actions) without actually loading the WordPress core at all.

      This is something I’m definitely going to look into. I’ve been on the fence about various ways to strictly test business logic outside of WordPress and I know PHPUnit is kind of the de-facto way of doing it, but when you’re also testing aspects of core it requires some heavy lifting from some of the tools mentioned in this post.

      As for using a layered approach for your architecture, apart from the general concepts of DDD, I am a big fan of the hexagonal architecture (http://fideloper.com/hexagonal-architecture), and the basic principle of controlling the flow of your dependencies like described here: https://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html .

      <3 DDD. I had a chance to take Eric Evan’s class years ago but that was when I was working with the enterprise. I still love many of the concepts but I’ve found it not always as easy to implement as some of his examples seemed to show (mainly because architecture in an existing code-base can be so much more complex or just different).

      Thanks for this post from Uncle Bob. He’s arguably my favorite software engineer author so I’m eager to read this.

       This is just a simplified example to explain what I mean, so don’t judge me for any of the above!

      No judgement at all — this makes a lot of sense. I like the ideas that you’ve laid out here.

      So here’s my question: Do you even think it’s worth testing to make sure actions have been loaded properly (say something as enqueuing scripts and/or styles) or just testing for custom actions and custom filters?

      I’m beginning to lean in the latter direction because the former are so fundamental to WordPress that I can’t imagine then not working. But the desire to have full coverage also trumps my more pragmatic desires, too.

      Thoughts?

  2. Ah, just another quick note…

    I see in your above screen shot that you’re using WP_UnitTestCase. This is actually meant to be used to create unit tests for WordPress core development. The simple fact that it loads the WordPress core for each tests makes it impossible to write unit tests with it, as you will never be able to test your code in isolation, only its integration with WordPress.

  3. Hey Tom,

    I still love many of the concepts but I’ve found it not always as easy to implement as some of his examples seemed to show (mainly because architecture in an existing code-base can be so much more complex or just different).

    I am currently working on a large-ish legacy code base where a WordPress installation has taken over more and more business processes.

    The concepts laid out in DDD are still valid here, although it does not make sense to attack the system as a whole. I have abstracted away some of the “membership” stuff into a domain model (was old code based on the now defunct StudioPress Premise plugin), and am now looking to get persistent event handling integrated (using http://simplebus.github.io/MessageBus/ & RabbitMQ). Most of all, once the domain is being used, I’ll add an anti-corruption layer, so that the legacy code will not corrupt the new design.

    This is really nice to work with, as I now can write the business logic in a very elegant and clear way ($member->getOrders()->renew('+ 1 year');). And if anything changes in the backend, the specific model implementation will get updated, not the business logic.

    So here’s my question: Do you even think it’s worth testing to make sure actions have been loaded properly (say something as enqueuing scripts and/or styles) or just testing for custom actions and custom filters?

    I would unit test my own code to make sure it makes the calls to add the actions and enqueue the scripts, and then I would integration test a typical page to see whether all was included as needed (this might be as simple as a HTTP GET + regex).

    Per definition, testing to make sure WordPress has received the actions / scripts cannot be done with unit tests, as you’re then testing an external system, instead of your own code.

    Tip: When you use real unit tests (that don’t run the entire WordPress system for each test), your tests will also run much, much faster, so that you can even consider adding them as a pre-commit check (I’m currently working on a standardized system for that using Composer: https://github.com/php-composter/php-composter#existing-php-composter-actions). The best tests are those that are actively being used.

    1.  I am currently working on a large-ish legacy code base where a WordPress installation has taken over more and more business processes.

      This sounds interesting enough in itself for a blog post all its own. Not because I don’t think it couldn’t be done (I absolutely do), but we need to have more people sharing this kind of stuff :).

      And if anything changes in the backend, the specific model implementation will get updated, not the business logic.

      This. This is why I think having a proper architecture for larger-ish projects is so important. Having those kind of changes is inevitable but being able to properly handle them pays dividends when enough time is spent on the front of the project to properly understand the domain so it can be modeled as close to the real world set up.

       I would integration test a typical page to see whether all was included as needed (this might be as simple as a HTTP GET + regex).

      Yep – makes perfect sense. 

      I constantly waver on the “should I unit test what I know works in nearly 100% of the cases?” but then I think “with my luck, there will be that 1% of the time it’s not working right because of some external factor about which I wasn’t aware.”

       Per definition, testing to make sure WordPress has received the actions / scripts cannot be done with unit tests, as you’re then testing an external system, instead of your own code.

      True, you’re right. I’m glad it’s here in the comments for clarification. This entire set of posts and post ideas has kind of taken on a life all its own since I started writing it so I’m playing a bit more loosely with the terms.

      Probably should fix that ;).

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.