Mocking at architectural boundaries: persistence and time

Posted on by Matthias Noback

More and more I've come to realize that I've been mocking less and less.

The thing is, creating test doubles is a very dangerous activity. For example, what I often see is something like this:

$entityManager = $this->createMock(EntityManager::class);

$entityManager->expects($this->once())
    ->method('persist')
    ->with($object);
$entityManager->expects($this->once())
    ->method('flush')
    ->with($object);

Or, what appears to be better, since we'd be mocking an interface instead of a concrete class:

$entityManager = $this->createMock(ObjectManagerInterface::class);
// ...

To be very honest, there isn't a big different between these two examples. If this code is in, for example, a unit test for a repository class, we're not testing many of the aspects of the code that should have been tested instead.

For example, by creating a test double for the EntityManager, we're assuming that it will work well with any objects we'll pass to it. If you've ever debugged an issue with an EntityManager, you know that this is a bad assumption. Anything may go wrong: a mistake in the mapping, missing configuration for cascading persist/delete behavior, an issue with the database credentials, availability of the database server, network connectivity, a missing or invalid database schema, etc.

In short, a unit test like this doesn't add any value, except that it verifies correct execution of the code you wrote (something a linter or static analysis tool may be able to do as well). There's nothing in this test that ensures a correct working once the code has been deployed and is actually being used.

The general rule to apply here is "Don't mock what you don't own" (see the excellent book "Growing Object-Oriented Software, Guided by Tests", or an article on the topic by Eric Smith, "That's Not Yours"). Whenever I've brought up this rule in discussions with developers, I've always met with resistance. "What else is there to mock?" "Isn't mocking meant to replace the slow, fragile stuff with something that is fast and stable?"

Mock across architecturally significant boundaries

Of course we want to use mocks for that. And we need to, since our test suite will become very slow and fragile if we don't do it. But we need to do it in the right place: at the boundaries of our application.

My reasoning for "when to mock" is always:

  1. If you encounter the need for some information or some action that isn't available to you in the memory of the the currently running program, define an interface that represents your query (in case you need to know something) or command (in case you want to do something). Put the interface in the "core" of your application (in the domain or application layer).
  2. Use this interface anywhere you want to send this query or command.
  3. Write at least one implementation for the interface, and make sure all the clients of the interface get this implementation injected as constructor arguments.

Mocking "persistence"

To fix the EntityManager example above we need to take a step back and articulate our reason for using the EntityManager in the first place. Apparently, we were in need of persisting an object. This is not something the running application could do naturally (the moment it stops, it forgets about any object it has in memory). So we had to reach across the application's boundaries, to an external service called a "database".

Because we always considering reusing things that are already available in our project, we just decided to go with the previously installed EntityManager to fulfill our needs. If however we would've followed the steps described above, we would've ended up in a different place:

  1. I need to persist (not just any object, but) my Article entity, so I define an interface that represents the action I intend for it to do:

    interface ArticleRepository
    {
        public function persist(Article $article): void
    }
    
  2. I use this interface everywhere in my code.

  3. I provide a default implementation for it, one that uses my beloved EntityManager:

    final class ORMArticleRepository implements ArticleRepository
    {
        public function persist(Article $article): void
        {
            // here we *will* use the EntityManager
        }
    }
    

Note that there's nothing about EntityManagers in the interface we've defined. Also, there's no generic object-type parameter, but a very specific one (Article). Finally, there's nothing "crazy" about the interface, like first having to call persist() and then flush(). It's now one thing: we want the ArticleRepository to take care of persisting an Article entity.

Mocking "time"

Another example: I need to know what the current date/time is. This information isn't available inside the running program; I need to reach outside of it. Normally, I'd simply summon the language's native date/time representation of "now", i.e. new DateTime('now'). Instead, following the same steps as described above:

  1. I define an interface that represents my query:

    interface Clock
    {
        public function currentTime(): DateTime;
    }
    
  2. I use this interface in my code:

    final class ArticleRepository
    {
        public function __construct(Clock $clock)
        {
            // ...
        }
    
        public function findNotYetPublishedArticles(): array
        {
            $now = $this->clock->currentTime();
    
            // look for articles that will be published
            // at some point in the future...
        }
    }
    
  3. I write a standard implementation for it, one that uses the most common way of determining the current date/time. This involves the system clock and the server's time zone:

    final class SystemClock implements Clock
    {
        public function currentTime(): DateTime
        {
            return new DateTime('now');
        }
    }
    

In unit tests for the core of your application, you can safely mock Clock to give you some deterministic return value. The unit test will be a pure unit test, since it won't involve a system call to get the current date/time (as opposed to when we'd just call new DateTime('now') on the spot).

Consider passing time as context

In terms of dependency injection versus context passing, you should also consider determining the current time once in the infrastructure layer (e.g. in the controller), and passing it as a value to any other objects that need this information.

Since we'll use a test double for Clock everywhere, the code in SystemClock won't be exercised during a test run. So we need another kind of test, a so-called integration test, which can prove that the SystemClock functions correctly, given an actual system clock is available to retrieve the current time.

Your unit tests will be fast, and your integration test will show you any mistakes in your assumptions about the third-party code or hardware that you're implicitly relying on.

Replacing language features

Another style of "mocking" time involves overriding built-in time-related functions. A pretty smart solution is provided by Symfony's PHPUnit Bridge library. It overwrites calls like time() and sleep(). Though this library is useful for testing low-level infrastructure/framework code, I don't recommend to use this library in scenarios that are like the one above, for two reasons:

  • Only part of the quality improvement comes from faster running test suites. The biggest improvement comes from defining your own abstractions. With it comes a more clear sense of what it is you need: do you need the current date, the current time, then with what precision, is a timezone involved, or even relevant, etc.
  • Replacing system calls involves assumptions about what the behavior in real life will be. These assumptions go untested and will result in hard-to-figure out bugs.

Mocking with architectural consequences

Another big benefit of this approach is that it forces you to think through what your significant architectural boundaries are; and enforce them with polymorphic interfaces. This allows you to manage the dependencies across those boundaries so that you can independently deploy (and develop) the components on either side of the boundary.

Uncle Bob, "When to Mock"

By following the above steps, you've basically been applying the Dependency Inversion Principle. And you'll be better off with that. The results are:

  • Loose coupling; you have the option to change the implementation, without touching all the clients of the interface.
  • Intention-revealing interfaces: you'll have better interfaces that define your questions in a better way than the framework/library/language feature's interface does.

If you keep making these interfaces for things that deal with "the world outside your application", you'll end up with two things:

  • An accidentally emerging infrastructure "layer", which contains all the code that connects to external services, data providers and devices.
  • An accidentally emerging hexagonal architecture architecture that is good for testing, since it allows using application features without all those slow and brittle external services, data providers and devices.

To me it's fascinating how proper techniques for creating test doubles could make a difference at the level of an application's architecture.

Conclusion

We've discussed two common areas of an application that need mocking - persistence and time. We've also seen how correctly mocking things has a healthy effect on your application's architecture. In another article we'll discuss two other things that need mocking - the filesystem, the network and... randomness.

PHP testing mocking hexagonal architecture architecture
Comments
This website uses MailComments: you can send your comments to this post by email. Read more about MailComments, including suggestions for writing your comments (in HTML or Markdown).
Paweł
Hi!In post above you wrote: 
Since we'll use a test double for Clock everywhere, the code in SystemClock won't be exercised during a test run. So we need another kind of test, a so-called integration test, which can prove that the SystemClock functions correctly, given an actual system clock is available to retrieve the current time.

Does this mean that you would advise against using time lock in integration tests? We have a maintenance mode functionality in the system (we create maintenance mode in CMS with start/end date). On the other hand, we have an endpoint, which returns information about the active maintenance mode. I would like to write integration test of this endpoint. I use https://github.com/lchrusciel/ApiTestCase library and using it I load fixtures with data from this maintenance mode. The problem is that they have fixed dates that do not cover the current date. In that case, this mock would work. Another approach is to create this maintenance object (and persist it) in the test - let's say with dates (start = -1 month, stop = +1 month from current date).What do you think is the better approach?
Vladislav Nikolayev

Hi Matthias!
Firstly want to say thanks for the books and posts you write. Totally love them.
What would you suggest for testing a service which relies on entity (doctrine) method call, which in turn uses date from private property, which cannot be altered directly? They are activatedAt and updatedAt properties, which are changed by some method calls, like activate(), or just by lifecycle events. Service relies on a method call, e.g. Fee::getNextPeriodDate(), which uses last period date, fee update date and date of account activation in it's algo.
As dependency injection obviously cannot be used for entity, I figured out only two possible solutions: reflection and mock. I went with reflection and have hidden it behind the builder (AccountBuilder::withActivationDate(), FeeBuilder::withUpdateDate()). But for some reason it still doesn't feel quite right as I need to know too much about the objects related to one I'm putting under test.

Matthias Noback

Thanks, that's great to hear! Actually, I usually provide an actual DateTime object as an argument for activate(). That way, instead of using reflection, you could just pass in the correct time as an argument. Does that help, or did I not understand your problem correctly?

Vladislav Nikolayev

Thanks for the reply! Yes, that helps, in case of activation, as I thought about the possibility to provide activate method with the date, but I wasn't sure if that's the way to do it.
But in activation there's at least a public method to alter state. What about, for example, a date of creation or update? Date of creation might be set only in constructor, and have a getter. There also could be object's behavior dependent on that date. Would the DDD way of testing a service dependent on that object's behavior be mocking a domain object's interface? But we would skip testing of an underlying algo of that method in that case. How do we test it then?
Or is it better to always provide a date to domain objects in case of creation or update? But that would require, for example, to add a date arg to every setter/behavior method.
It's really interesting how you usually get around a problem like this.
It also looks like a topic for a separate post about testing datetime related behavior of domain objects in services. Couldn't find a lot of info on the topic as most of posts talk about services depending on date directly.

Matthias Noback

Yes, I think that every bit of an entity's behavior should be defined by just the object and the way you can interact with it. Which is why lifecycle events are something I don't use. So yes, I always provide all the data explicitly. That way, you have full control, and your object is completely unit-testable.

Maybe my latest book can help you with some of these questions as well (https://www.manning.com/boo....

Vladislav Nikolayev

Thanks for insights on your approach. I had read that book cover to cover, but I guess it's never late to reread it, considering that you've made some updates since I bought it. :)

Matthias Noback

Oh, hehe, in that case, I'm not sure if reading it again will be as helpful :)

Вячеслав Громыко

Hi Matthias. Your blog one of the best site about php now. I have a question about mocking.
I have a repository class, for example:
final class PatientRepository
{
public function getActivePatients()
{
// return array of patient entities.
}
}

I need to get the results of the function in my unit test.
What I can do with this problem:
1. Mock class PatientRepository and mock function results. But I decided to minimum use mocks in my tests.
2. Add PatientRepositoryInterface. And create an anonymous class in my test case that implements this interface. But what happened when this interface extended by additional methods? I should add these methods to all anonymous classes that I added?
3. Maybe I should add a small interface that contains only getActivePatients function, but it looks very tricky.

Can you help me with my question?

Matthias Noback

You should have an interface for the repository. Then you can create a stub for the interface, which would be the same as providing an alternative implementation for it. Your concern about the extra methods is addressed by building a partial stub, using for instance PHPUnit's mock builder.

Rasmus Schultz

One thing I dislike is the introduction of an interface to enable the test-case. You introduce arbitrary abstractions everywhere, for things for which only a single, real implementation exists. This bothers me, because you didn't need an abstraction - there was no domain requirement driving that decision, so you're letting the needs of the test drive your architectural decisions.

This only affects architecture in a small way, I know - but enough that it bothers me. What do I write in the doc-block of that interface to explain why it exists? I didn't introduce this abstraction because there's a meaningful division of responsibilities, or because the abstraction represents something relevant to my domain. It's a 1:1 clone of the public interface of the service itself, and it's merely an artifact of a strictly technical requirement.

It bugs me. I wish I knew a better way.

Matthias Noback

Thanks for commenting here.
The technical requirement is not a goal per se; the goal is to end up with a top infrastructure layer you can replace by one that allows you to test your application in more and better ways (see "Hexagonal architecture").

The fact that a class has the same public interface as the interface it implements is by no means something bad. What's important is that the class has implementation details, whereas the interface doesn't. This makes the class concrete, full of low-level stuff, and the interface abstract, only containing the high-level stuff. Check out "Dependency Inversion Principle" for more about this: classes should depend on things that are more abstract/high level.

Rasmus Schultz

I think I just realized why Dart lets you implement the public interface of a class 🙄

Fesor

about mocking time: there were article by J.B. Rainsberger (blog.thecodewhisperer.com/p... about mocking and one of the options to provide abstraction for time is to represent it as stream of timestamps instead of Clock.

Matthias Noback

Thanks, I'll check it out.

manhee

What's going on with php world, does everything has to be so complicated now? Write interface and class to just get date('Y-m-d H:i:s')...

Rasmus Schultz

Here's what I usually do: I add a protected getTime() method where needed. Then simply extend the class, override the method and return a fixed timestamp - and override the registration of that service with the extended one under test. If you use an anonymous class, the whole thing is like 3 lines to set up. Works for simple cases at least - more complex projects might benefit from a time service.

Sebastiaan Stok

This is also what Uncle Bob martin recommends, sometimes you need to add some protected methods to make testing possible or easier. By keeping them protected they remain a detail which helps the overall stability of the tests.

Sergey Telshevsky

What will you do when 20 developers will work on your project and you'll have to test things like recurring payments, scheduled notifications, etc.? It all depends on the context, with the context being the project you build. If it's a homepage for your friend - I doubt it needs interfaces & tests at all, but if it's a financial business, then yes, you better use all techniques above.

Matthias Noback

Totally agree, don't over-complicate. I'm just sharing insights from my point of view (the projects I've encountered, the experiences I've had with teams and projects) and I hope that they will some day be useful for you.

ivorobioff

An ability to persist new entities and then to flush the changes has its own advantages. For example, before flushing, one might want to create 2 entities, update another 3 entities and then flush all the changes all together. Also, I usually mock the stuff to get dummy objects instead of real once, and not to test correctness of code flow. With that, I don't really see advantage of your approach and disadvantage for simply mocking the entity manager.

Additionally, I rarely see an opportunity to use dedicated repositories if focusing all business logic into dedicated services.

All in all, thank you for the article, it gave me food for thought ;-)

Seb Schuh

for that purpose my repositories have a persistAll/saveAll method

Matthias Noback

Thanks for commenting.
I've been following a strict separation of application/core vs infrastructure code, and so, where possible, I don't consider transaction management part of my application code. In fact, the only implicit rule comes from DDD's approach to aggregate modeling: an aggregate (~= entity) forms the transaction boundary. That means, each transaction creates or makes changes to only one aggregate. In my experience this works great, but it comes with some "side-effects", like the need for eventual consistency.
Another thing; I don't use mocking for objects other than "do-ers" (services). An entity, a value object, a DTO, etc. should be created on the spot. Mocking them is quite dangerous, as their actual behavior can't be described in as concise an interface as that of a service. I guess this deserves some further blogging as well. Anyway, there may already be good resources on this topic.

Cherif BOUCHELAGHEM

Thank you for the detailed article and resources, doubles are great, they help to get adapters for external dependencies, and Phpspec is powerful for that.
I have a question about doctrine EntityManager/EntityRepository, what is better to implement repositories? Extending EntityRepository or injecting the EntityManager?

For me I always extend the EntityRepository, I saw somewhere people advice to inject EntityManager!

Matthias Noback

You already found the related article (https://matthiasnoback.nl/2...) I see :) Yes, the way to go here is to define your interface, create a class that implements this interface and *inject* anything this class needs (e.g. the entity manager). If your repository should be able to survive a failed database transaction (which "closes" the entity manager and makes it useless), you should inject the manager registry instead (https://matthiasnoback.nl/2...). This way, you'll always get a non-closed entity manager.

Cherif BOUCHELAGHEM

thank you for the tip I will try it for sure :)

Matthias Noback

No problem, thanks for commenting!

Sean Mumford

I always inject the entity manager, since extending the entity manager means my interface now has a bunch of "undocumented" public methods that another developer might use, which would fail if we ever switched implementations. Here's an example from some real-world code: https://ghostbin.com/paste/...

Cherif BOUCHELAGHEM

Interesting! Thank you :)

Sean Mumford

Also added to Pastebin since the previous paste expires in two weeks: https://pastebin.com/Y2rFnkSs

Cherif BOUCHELAGHEM

I took a look to EntityRepository class in doctine code and I think implementing an interface that represent the business and extending the EntityRepositoy is the way to go, EntityRepository already has EntityManager injected https://github.com/doctrine...
https://github.com/doctrine...

Sean Mumford

Make sure you don't accidentally use one of the EntityRepository's public methods. Otherwise, a change in Doctrine or a change in your Persistance Layer will break your application and your unit tests won't catch it. Personally I inject it to avoid that risk but to each their own!

Matthias Noback

Of course it's possible to extend the EntityRepository, but it's not useful at all. As you mention, it only adds the risk that people will start using methods that aren't part of your manually defined interface.

Cherif BOUCHELAGHEM

Oh man you have a great article about it https://matthiasnoback.nl/2... :)
many thanks