Growing Object-Oriented Software, Guided by Tests Without Mocks

By Vladimir Khorikov

This is a review of the Growing Object-Oriented Software, Guided by Tests book (GOOS for short) in which I’ll show how to implement the sample project from the book in a way that doesn’t require mocks to be tested.

Growing Object-Oriented Software, Guided by Tests Without Mocks

Let me first explain why I’m doing a review of this book. If you read this blog regularly, you probably noticed that I’m pretty much against using mocks in tests. Because of that, I sometimes receive feedback which states that the code I’m taking as an example just doesn’t employ mocking correctly and thus my arguments are not quite valid.

So here I’ll take the most canonical example of using mocks I could find – the one from the GOOS book – and will show how the use of mocks damages the design. I’ll also show how much simpler the code base becomes when you get rid of mocks and apply the guidelines I described in the previous posts of this series.

The second reason for this review is to point out which advice in the book I consider good and which not. There are plenty of great tips and guidelines in the book, and there is also advice that can harm your application should you implement it in practice.

The good parts

Alright, I’ll start with the good parts, most of which reside in the first two sections of the book.

The authors put a great emphasis on the tests being a safety net which helps reveal regression errors. This is indeed an important role of them. In fact, I think it’s the most important one. It provides confidence which, in turn, enables fast movement towards the business goals. It’s hard to overestimate how much more productive you become when you are sure that the feature or refactoring you just implemented doesn’t break the existing functionality. This feeling is liberating.

The book also describes the importance of setting up the deployment environment in early stages. That should be your first priority when starting a green field project as it allows you to reveal potential integration issues right away, before you write a substantial amount of code.

To do that, the authors propose building a “walking skeleton” – the simplest version of your application possible which at the same time crosses all layers end-to-end. For example, if it’s a web application, the skeleton can show a simple HTML page which renders some string from the real database. This skeleton should be covered with an end-to-end test which should become the first test in your test suite.

This technique allows you to focus on building a deployment pipeline without paying too much attention to the application’s architecture. The faster you get feedback, the better.

The book proposes a two-level TDD cycle:

Growing Object-Oriented Software, Guided by Tests Without Mocks: Two-level TDD cycle

Two-level TDD cycle

That is starting each feature with an end-to-end test and building your way to passing it with the regular red-green-refactor cycle.

End-to-end tests here act more as a progress measurement and some of them may fail because the features they cover are still in development. Unit tests, at the same time, is a regression suite and should pass at all times.

It’s important that end-to-end tests touch as many external systems as possible. It would help you reveal integration issues. At the same time, the authors admit that you’ll often have to fake some external systems anyway. It is an open question what to include into end-to-end tests; you need to make a decision in each case separately. For example, if you work with an external banking system, it’s not practical to create real money transactions every time you want to test integration with it.

The book proposes to extend the classic 3-step TDD cycle with an additional step: clearing up the diagnostics message.

Growing Object-Oriented Software, Guided by Tests Without Mocks: additional TDD step

Additional TDD step

This practice helps make sure that when the test fails, you will be able to understand what’s wrong with your code just by looking at the failure message and won’t have to launch the debugger.

The authors advice to develop the application end to end starting from the beginning. Don’t spend too much time on polishing your architecture, start with some input that comes from the outside world (for example, from the UI) and process that input entirely, down to the database level with the minimum amount of code possible. In other words, work with vertical slices of functionality, don’t build the architecture up-front.

Another great advice is to unit-test behavior, not methods. They are often not the same thing as a single unit of behavior may cross multiple methods at once. This practice will help you build tests that answer the “what” question instead of “how”.

Another interesting point is context-independence of the SUT:

Each object should have no built-in knowledge about the system in which it executes.

That is basically the concept of domain model isolation. The domain classes shouldn’t know or depend on any external system. You should be able to extract it out of the context in which it runs without additional effort. Aside from the ability to easily test the code base, such technique greatly simplifies it as you are able to focus on your domain model without paying attention to concerns not related to your domain.

The book proposes the somewhat famous rule “Only mock types that you own”. That is, you should use test doubles to substitute only the types you created yourself. Otherwise, you are not guaranteed to mimic their behavior correctly. The guideline basically boils down to writing your own gateways for each external service you use.

It’s interesting that the authors break this rule for a couple of times throughout the book. The external types in those cases are quite simple, though, so there’s not much point in substituting them with own implementations.

By the way, Gojko Adzic in his talk Test automation without a headache refines this rule to “Only mock types that you understand”. And I think this version better fits the intention behind the guideline. If you fully understand how the type works, it doesn’t matter who its author is, you are able to fully simulate its behavior with a mock and thus don’t need any additional wrappers on top of it.

The bad parts

Despite all the great tips and techniques the book proposes, the amount of potentially harmful advice is quite substantial as well.

The book is a strong proponent of the collaboration verification style of unit testing even when it comes to communication between individual objects inside the domain model. In my opinion, it’s the main shortcoming of the book. All other shortcomings flow from this one.

To justify this approach, the authors allude to the definition of Object-Oriented Design given by Alan Kay:

“The big idea is “messaging” […] The key in making great and growable systems is
much more to design how its modules communicate rather than what their internal
properties and behaviors should be.”

They then conclude that interactions between objects is what you should focus on foremost in unit tests. By this logic, the communication pattern between classes is what essentially comprises the system and identifies its behavior.

There are two problems with this viewpoint. First, I wouldn’t bring the Alan Key’s definition of OOD here. It’s quite vague to build such a strong argument upon and has little to do with how modern strongly-typed OOP languages look like today.

Here’s another famous quote of him:

“I made up the term ‘object-oriented’, and I can tell you I didn’t have C++ in mind”.

And of course, you can safely substitute C++ with C# or Java here.

The second problem with this line of thinking is that separate classes are too fine-grained to treat them as independent communication agents. The communication pattern between them tend to change often and has little correlation with the end result we should aim at verifying.

As I mentioned in the previous post, the way classes inside the domain model talk to each other is an implementation detail. The communication pattern only becomes part of API when it crosses the boundary of the system: when your domain model starts interacting with external services. Unfortunately, the book doesn’t make this distinction.

The drawbacks of the approach the book proposes become vivid when you consider the sample project it goes through in the 3rd part. Not only focusing on collaboration between classes entails fragile unit tests that couple to the SUT’s implementation details, but it also leads to an overcomplicated design with circular dependencies, header interfaces, and an excessive number of layers of indirection.

In the rest of this article, I’m going to show you how the book’s implementation can be modified and what effect that modification has on the unit test suite.

The original code base is written in Java, the modified version is in C#. Note that it is a full rewrite, not just a partial one. I took the problem from the book and implemented it from scratch up to the point where the book left it off. It means that along with the domain model and the unit tests, there also are end-to-end tests which work with UI and an external XMPP server. The UI is written in WPF; as for the XMPP server, I wrote a simple emulator using named pipes.

The sample project

Before diving into the code base, let me introduce the problem domain first. The sample application is Auction Sniper – a robot aimed at participating in online auctions. Here’s its UI interface:

Growing Object-Oriented Software, Guided by Tests Without Mocks: UI interface

UI interface

Item Id is the identifier of an item that is being sold; Stop Price is the maximum price you as a user are willing to pay for this item; Last Price is the latest bid amount came from the auction server; Last Bid is the latest bid Auction Sniper made on your behalf; State is the state of the auction. In the example above, you can see the application won both of the items, that’s why the last prices equal to the last bids – both came from our software.

Each row in the grid here represents a separate agent which listens to the events coming from the auction server and responds to them accordingly. The business rules can be summarized with the following picture:

Growing Object-Oriented Software, Guided by Tests Without Mocks: State machine

State machine

Each agent (they are also called Auction Snipers) starts from the top of the picture, in the Joining state. It then waits for the server to send an event about the current state of the auction – what the last price was, from what user, and what the minimum increment should be in order to outbid the last price. This event is named Price event.

If the bid required is less than the stop price we as a user set up for this item, the application sends a bid and transitions to the Bidding state. If a new price event shows that our bid is leading, Sniper does nothing and moves to the winning state. Finally, the second event the auction server can send is the Close event. When it comes, the application is looking at what state it is currently in for the given item. The Winning state goes to Won, and all others go to Lost.

So basically what we have here is an automatic auction participant that sends commands to the server and maintains an internal state machine.

Let’s now look at the architecture the book came up with. Here’s the diagram (click to enlarge):

Growing Object-Oriented Software, Guided by Tests Without Mocks: Architecture from the book

Architecture from the book

If you think it is too complicated for such a task at hand, that’s because it is. So, what issues do you see here?

The very first thing that meets the eye is lots of header interfaces. That are interfaces that fully mimic a single class that implements them. For example, the XMPPAuction class has a 1-to-1 correspondence to the Auction interface, AcutionSniper – to AuctionEventListener, and so on. Interfaces with the only implementation don’t represent an abstraction and generally considered a design smell.

Below is the same diagram without interfaces. I removed them so you have less visual clutter:

Growing Object-Oriented Software, Guided by Tests Without Mocks: Classes without interfaces

Classes without interfaces

The second issue here is cyclic dependencies. The most obvious one is between XMPPAuction and AuctionSniper, but there are several others as well. For example, AuctionSniper refers to SnipersTableModel which refers to SniperLauncher, and so on until the connection comes back to AuctionSniper.

Cyclic dependencies add tremendous cognitive load when you try to read and understand the code. The reason here is that, with such dependencies, you don’t know where to start from. In order to understand what one of the classes does, you need to push the whole graph of its siblings into your head.

Even after I rewrote the original implementation, I often had to refer to the diagram to understand how different classes and interfaces relate to each other. We as humans are good at processing hierarchies, not graphs. Scott Wlaschin has a great article that dives into the details of this subject: Cyclic dependencies are evil.

The third problem is lack of the domain model isolation. Here’s how the architecture looks like from a DDD perspective:

Growing Object-Oriented Software, Guided by Tests Without Mocks: lack of the domain model isolation

Lack of the domain model isolation

The classes in the middle of it comprise the domain model. At the same time, they communicate with the Auction server (on the left) on one hand, and with the UI (on the right) on the other. For example, SniperLauncher talks to XMPPAuctionHouse, and AuctionSniper – to XMPPAcution and SnipersTableModel.

Of course, they do it by using interfaces, not the actual classes but again, introducing a header interface for something doesn’t mean you automatically start adhering to the Dependency Inversion principle. It’s just a hack to avoid reaching genuine domain model isolation.

Ideally, the domain model should be self-contained, classes inside of it shouldn’t talk to classes from the outside world, not using concrete implementations, nor their interfaces. Proper isolation would mean the domain model can be tested in a functional manner without involving mocks.

These shortcomings is what you often end up with when you focus on testing collaborations between classes, and not their public API. Such attitude leads to creating header interfaces because otherwise, it’s impossible to mock those classes out. Also, you have lots of circular dependencies and you have domain entities communicating directly to the outside world.

Let’s now look at the unit tests themselves. Here’s one:

@Test public void reportsLostIfAuctionClosesWhenBidding() {

  allowingSniperBidding();

  ignoringAuction();

 

  context.checking(new Expectations() {{

    atLeast(1).of(sniperListener).sniperStateChanged(

      new SniperSnapshot(ITEM_ID, 123, 168, LOST));

  

    when(sniperState.is(“bidding”));

  }});

 

  sniper.currentPrice(123, 45, PriceSource.FromOtherBidder);

  sniper.auctionClosed();

}

Putting away its collaboration nature which means that we need to create and support a substantial amount of mock machinery just to verify the communication pattern, here we have implementation details leaking into the test. The when statement means that the test becomes aware of the internal SUT’s state and mimics that state in order to perform the verification.

Here’s another example:

private final Mockery context = new Mockery();

private final SniperLauncher launcher =

  new SniperLauncher(auctionHouse, sniperCollector);

private final States auctionState =

  context.states(“auction state”).startsAs(“not joined”);

 

@Test public void

addsNewSniperToCollectorAndThenJoinsAuction() {

  final Item item = new Item(“item 123”, 456);

 

  context.checking(new Expectations() {{

    allowing(auctionHouse).auctionFor(item); will(returnValue(auction));

   

    oneOf(auction).addAuctionEventListener(with(sniperForItem(item)));

    when(auctionState.is(“not joined”));

 

    oneOf(sniperCollector).addSniper(with(sniperForItem(item)));

    when(auctionState.is(“not joined”));

   

    one(auction).join(); then(auctionState.is(“joined”));

  }});

 

  launcher.joinAuction(item);

}

This sample is a clear example of implementation details leakage. Here, the test implements a fully-fledged state machine in order to verify that the methods are invoked in this particular order (highlighted):

public class SniperLauncher implements UserRequestListener {

  public void joinAuction(Item item) {

    Auction auction = auctionHouse.auctionFor(item);

    AuctionSniper sniper = new AuctionSniper(item, auction);

    auction.addAuctionEventListener(sniper);

    collector.addSniper(sniper);

    auction.join();

  }

}

Because of the coupling to the SUT’s internals, tests like this one are extremely fragile. Any non-trivial refactoring will make them fail regardless of whether that refactoring broke something or not. It significantly diminishes their value. Such tests result in lots of false positives and because of that cannot act as a reliable safety net. Here you can read more on what I consider a valuable unit test suite: Unit tests value proposition.

The full source code for the book’s sample project can be found here.

Alternative implementation with no mocks

Alright, it’s a lot of strong statements so far. Clearly, I need to provide an alternative solution in order to back up my words with some real code. You can find the full source code of that solution on GitHub. I’ll now describe its architecture and the reasoning behind it.

To understand how the project can be implemented with a proper domain model isolation, without circular dependencies and without excessive number of needless abstractions, let’s consider what responsibilities AuctionSniper has. It receives events from the server and responds back with some commands, maintaining an internal state machine along the way:

Growing Object-Oriented Software, Guided by Tests Without Mocks: AuctionSniper responsibilities

AuctionSniper responsibilities

And that’s basically it. In fact, it’s almost ideal functional architecture and there’s nothing preventing us from implementing it as such.

Here’s the class diagram for the alternative architecture:

Growing Object-Oriented Software, Guided by Tests Without Mocks: alternative architecture

Alternative architecture

Several points to note here. First of all, the domain model is fully isolated from the outside world. The classes in it don’t talk to the view model or the XMPP Server, all references point to the domain classes, not the other way around.

All communication with the outside world, be it the auction server or UI, is handled by the Application Services layer which in our case is AuctionSniperViewModel. It acts as a shield that protects the domain model from the unwanted influence. It filters the incoming requests and interprets the outcoming responses.

Secondly, the domain model doesn’t contain any cyclic dependencies. The class structure is a tree which means that a potential reader of this code has a clear place to start learning it from. He can proceed from the leafs of the tree up to its roots step by step, without having to push the whole diagram into his head. This code base is pretty simple of course, and I’m sure you wouldn’t have any troubles reading it even in the face of circular dependencies. In more complex projects, however, a clear tree-like class structure gives a great benefit in terms of simplicity.

By the way, the famous DDD pattern – Aggregate – is aimed at solving this exact problem. By having multiple entities grouped into a single aggregate, we reduce the number of connections in the domain model and thus make the code base simpler.

The third point to note here is that the code base above doesn’t have any interfaces. Not a single one. This is one of the benefits of having a fully isolated domain model: you just don’t need to introduce interfaces unless they represent a real abstraction. Here, we don’t have any.

Note that with this architecture, we adhere to the guideline I described in the Pragmatic unit testing article. Classes in it either contain business knowledge (the classes inside the domain model) or communicate to the outside world (the application services layer), but never both. With such separation of concerns, we are able to focus on one thing at a time: we either think about the domain logic or decide how to respond to the stimulus from the UI and the auction server.

Again, this leads to greater simplicity and, therefore, better maintainability. Here’s how the most important part of the Application Services layer looks like:

_chat.MessageReceived += ChatMessageRecieved;

 

private void ChatMessageRecieved(string message)

{

    AuctionEvent ev = AuctionEvent.From(message);

    AuctionCommand command = _auctionSniper.Process(ev);

    if (command != AuctionCommand.None())

    {

        _chat.SendMessage(command.ToString());

    }

}

We get a string from the auction server, transform it to an event (the validation is baked into this step), feed it to the auction sniper and if the resulting command is not None, send it back to the auction server. As you can see, the lack of business logic makes the Application Services layer trivial.

The idea with the dump Application Services layer is very similar to the idea with dump Mutable Shell which I described in my immutable architecture post.

Test suite without mocks

Another benefit of having an isolated domain model is the ability to test it using the functional style of unit testing. We can treat each piece of behavior in isolation and verify the end result it produces, without paying attention to how that end result is achieved. And in doing so, we are able to operate the existing domain concepts making the tests extremely readable.

For example, this test checks how Sniper that just joined to an action behaves after getting a Close event:

[Fact]

public void Joining_sniper_loses_when_auction_closes()

{

    var sniper = new AuctionSniper(“”, 200);

 

    AuctionCommand command = sniper.Process(AuctionEvent.Close());

 

    command.ShouldEqual(AuctionCommand.None());

    sniper.StateShouldBe(SniperState.Lost, 0, 0);

}

It verifies that the command the sniper responds with is empty, meaning that the sniper doesn’t take any action, and the state of the sniper is Lost after that.

Here’s another example:

[Fact]

public void Sniper_bids_when_price_event_with_a_different_bidder_arrives()

{

    var sniper = new AuctionSniper(“”, 200);

 

    AuctionCommand command = sniper.Process(AuctionEvent.Price(1, 2, “some bidder”));

 

    command.ShouldEqual(AuctionCommand.Bid(3));

    sniper.StateShouldBe(SniperState.Bidding, 1, 3);

}

This one makes sure the sniper bids when the current price of the lot and the minimum increment is lower than the stop price.

The only place where mocks would potentially be justified is when testing the Application Services layer which communicates with the external systems. But as this part is covered by end-to-end tests, it is not needed. By the way, the end-to-end tests in the book are great, I haven’t found anything to improve or change in them.

Conclusions

Not only does focusing on the communication pattern between individual classes lead to fragile unit tests, it also entails architectural damage (one could call it test-induced design damage).

To avoid that damage, make sure you:

  • Don’t create header interfaces for your domain classes.
  • Minimize the number of cyclic dependencies in code.
  • Properly isolate the domain model: don’t allow domain classes to communicate with the outside world.
  • Flatten the class structure and reduce the number of layers of indirection.
  • Focus on output and state verification when unit testing the domain model.

This post might seem a bit harsh but no offense is intended. Despite all said above, the book has lots of extremely valuable material. It’s just you need to carefully separate it from the other parts.

If you enjoyed this article, be sure to check out my Pragmatic Unit Testing Pluralsight course where I show the full process of refactoring the original architecture from the book.

Source code

I recommend you to take a closer look at the alternative implementation. It’s actually quite simple. When working on it, I spent most of the time writing an emulator for the XMPP client and server (which you don’t need to look at anyway), the other parts are simple and straightforward.

Other articles in the series





  • Dragan Stepanovic

    Vladimir,

    Thanks for the nice post, it definitely gives another perspective of the OI TDD style presented in GOOS.
    Also, I’d agree that injecting interfaces to all of the classes can be overflexibility and I think approach with mocking all collaborators can produce fragile test suites.
    However, I have problem with a design of this method call:
    AuctionCommand command = _auctionSniper.Process(ev);
    and by my opinion this design is a mandatory side effect of forcing a mocksless approach in a functional manner.
    Someone would say that this violates CQS, but I think that’s the least problem here.
    My main problem with this is that the design of the method forces me, as a client of the method, to accept the side effect of processing and forces me to take a responsibility of having to know how to handle it (where to send or log the command etc.). I, as a client, don’t want to accept this side effect. I just want to send the command. Whatever is a side effect is not of my concern. Also, if I think about the situation where there are multiple clients, all of them will be forced to have this responsibility.
    As well, for me there’s no essential difference in asserting against the return value of the Process method, versus matching the command that’s sent to the (mock) listeners at the boundary of application, which handle the produced command.
    My opinion is that mocks shouldn’t necessarily be avoided. I think they have their proper and justifiable use and that’s in the case when results of the commands go outside of the system. For example, if a effect of processing a command is sending a mail through a mail sender, I’d use mock for a mail sender and verify the mail that is sent.
    On the other hand, I’m against the use of the test approach where a SUT is always one class (which is used in the GOOS book), since this produces fragile test suites.
    Thanks again for the article.

    • http://enterprisecraftsmanship.com/ Vladimir Khorikov

      Hi Dragan, thank you for the comment.

      I agree that CQS is violated here, and it would be nicer to keep the side effects out of AuctionSniper. However, the implementation without side effects would be awkward in C# and Java as they don’t have immutability support like other functional languages. So I in my opinion the violation is justified here. CQS is not a strong principle anyway, it’s more of a “nice to have” one (think of stack.Pop()).

      My main problem with this is that the design of the method forces me, as a client of the method, to accept the side effect of processing and forces me to take a responsibility of having to know how to handle it (where to send or log the command etc.). I, as a client, don’t want to accept this side effect. I just want to send the command. Whatever is a side effect is not of my concern

      Well, you will have to have this logic somewhere anyway. My point is that the logic of generating the side effect (the command) should be separated from the logic of applying that side effect. Such separation results in many benefits. One of them is having a seam which you can use to test the generation logic. That is basically how applications built in a functional manner work. They usually have two parts: pure core that generates some side effects and impure shell that knows how to apply them to the outside world.

      Also, if I think about the situation where there are multiple clients, all of them will be forced to have this responsibility.

      Not sure I understand why this is a bad thing. If those clients have different perspectives on how the side effects (the commands) should be applied, they will need to have their own version of this logic anyway. Putting that logic to separate clients will bring explicitness to it.

      As well, for me there’s no essential difference in asserting against the return value of the Process method, versus matching the command that’s sent to the (mock) listeners at the boundary of application, which handle the produced command.

      The Assert parts haven’t gained much in my alternative implementation, indeed. However, if you consider other parts which have been changed after removing mocks, the benefits are quite substantial. For example, the Arrange parts in unit tests were reduced to just a couple of lines. Also, this is true only for AuctionSniper tests; tests for the other 2 domain classes (AuctionEvent and AuctionCommand) have been simplified in both the Arrange and Assert parts.

      I think they have their proper and justifiable use and that’s in the case when results of the commands go outside of the system.

      I agree with that. I noted in the article that this would indeed be a justifiable use case for mocks. Using mocks (along with other test doubles) is the only way to verify how the application works with the external systems. I also wrote about it here: http://enterprisecraftsmanship.com/2016/06/21/pragmatic-integration-testing/

  • Bill Schneider

    I have also started to question the value of mock and collaboration-heavy unit tests.

    Have you seen this article: https://news.ycombinator.com/item?id=11799272

    The key phrase for me is that your unit tests should focus on cases where there is an “independent oracle of correctness, and for which there is ascribable business value”

    In other words, tests that prove a method calls some other method are not as useful as behavioral/functional-style tests that demonstrate that given an input, you get a certain known output.

    • http://enterprisecraftsmanship.com/ Vladimir Khorikov

      No, I haven’t seen this article, will read it shortly, thank you for the reference.

      In other words, tests that prove a method calls some other method are not as useful as behavioral/functional-style tests that demonstrate that given an input, you get a certain known output.

      I agree with that completely. To me, the way objects communicate with each other is an implementation detail and have nothing to do with the actual result the SUT produces. I also like to say (or maybe I heard it from someone) that BDD is TDD made right. In other words, any tests, in each level, should aim at verifying the end result as it seems from the end user’s perspective, without paying attention to how that result is achieved.

  • Srikanth

    Awesome review of GOOS. Thanks a lot. “…the authors propose building a “walking skeleton” – the simplest version
    of your application possible which at the same time crosses all layers
    end-to-end” – this line makes a lot of sense to me right now.

    • http://enterprisecraftsmanship.com/ Vladimir Khorikov

      Thanks! Glad it was helpful!

  • Guillaume L

    Vladimir,

    What are the arrows in your class diagram ?

    There’s no reference from XMPPAuction to AuctionSniper nor from AuctionSniper to SnipersTableModel if you look at the actual code here or in the book. Same for SnipersTableModel and SniperLauncher.

    Why the arrows ?

    • http://enterprisecraftsmanship.com/ Vladimir Khorikov

      The reference from AuctionSniper to XMPPAuction is through the Auction interface, the reference from AuctionSniper to the table model is through SniperListener. Here’s the original diagram from the book you posted awhile ago. It lacks some elements but the connections are there as well: https://a.disquscdn.com/uploads/mediaembed/images/3821/4078/original.jpg

      • Guillaume L

        Your diagram is biased, this one is closer to the truth. You should make the arrows point to the interfaces, not the concrete classes.

        You give the false impression that the classes are tightly coupled when they are not. The interfaces are there for a reason. They sit across a boundary between the XMPP world and the Auction Sniper world. This is a perfectly valid reason for having what you call a “head interface”.

        I haven’t found it a “tremendous cognitive load” to reason about this system. You always know where you start from since the business domain dictates it. You have two streams of communication – one that comes from the remote auction server into the auction sniper system and the other from the system to the remote server. It’s as simple as that.

        The authors could have split XMPPAuction in two, but that would have damaged the important domain concept of an Auction in my opinion. A remote auction is an event where bidding messages go back and forth. It is bidirectional in nature. If you look at the Auction interface, I find it pretty cohesive.

        • http://enterprisecraftsmanship.com/ Vladimir Khorikov

          The only reason I put the diagram this way is because it would be hard to understand it otherwise. Head interfaces (the term is not mine BTW) would be fine if they represent something that doesn’t contain domain knowledge. Introducing head interfaces for classes that are part of the domain model is never a good idea in my opinion.

          They sit across a boundary between the XMPP world and the Auction Sniper world.

          Not all of them. SniperCollection resides inside the domain model. That’s why I wrote my own diagram, the original one lacks some elements: https://i.imgur.com/gLnbFGt.png

  • Guillaume L

    Vladimir,

    It seems like you’re suffering from some kind of confirmation bias about mocks that led you to a series of unjustified criticisms and non sequiturs here.

    * Your arguments miss the point about the real structure and intent behind the application described in GOOS.

    As I already wrote in a previous comment, the first test you point your finger at uses mocks for a reason. It’s because the tested interaction crosses boundaries. As you surely noticed, there are 2 folders in the code base that represent 2 “adapter” modules or outer layers – UI and XMPP. The collaborator in that test is a SniperListener, whose implementation is to be found in one of these 2 outer layers – namely, in the UI part (SnipersTableModel). Now I think you admitted yourself that mocks can be useful at application seams. This is a perfect example of it. The core of the domain, the AuctionSniper, notifies external listeners which it knows nothing about, that stuff changed. You do not know what concrete instance will be behind the listener, it could be pretty slow or faulty in reality, and mocking is a good way to verify that it was notified.

    Your new implementation for that part of the sytem, in contrast, has two major flaws :

    – You don’t verify that specific interaction other than through end-to-end tests that run against the UI (so one particular implementation of a listener), thus much slower and carrying the extra complexity of a UI library dependency.

    – You make AuctionSniperViewModel (the UI) get and decypher an XMPP auction message string directly by using AuctionEvent.From() which is a total nonsense in a DDD setting where the central source of truth is supposed to be the domain and the peripheral layers are not supposed to know about each other. It is also a waste of the authors efforts to build a graph composed of well-defined, meaningful, cohesive actors. You’re basically doing Chat > UI > Domain “helper” instead of the original Chat > Translator > AuctionSniper > UI which both didn’t short-circuit the domain and made the concept of translator explicit (it is not the only rich business concept you lost in the process by the way – see Portfolio)

    Another incoherence between the code and your line of thought is that in another place you do use a kind of mock – FakeAuction. It verifies what it received just like a mock would. Now you could argue that this isn’t a mock per se but a spy or whatever it is called in some nomenclature, but its function is exactly the same – you could perfectly replace it with a library-provided mock and all people using library-provided mocks could perfectly use these hand-written fakes instead, it wouldn’t change a thing about the outcomes of the tests involving them.

    The second test you mention is around SniperLauncher. This class is strangely reminiscent of an Application Service in a domain-driven design system. It gets an auction from a kind of repository (AuctionHouse). Then it news up a domain object, does something with that object in relationship with the auction and calls another method on the auction. Not putting it in an Application layer is a mistake by DDD standards, but GOOS isn’t about DDD and doesn’t explicitly use any of the DDD tactical patterns. Here again, the SUT makes calls across boundaries since the AuctionHouse and Auction implementation are in the XMPP layer. And I don’t find it shocking to test the sequence of calls performed by this Launcher “service” since its job is to be an orchestrator. Note that the authors decided to refactor it that way, they were not forced to do it because of mocks. The order of calls is important here since we want all the subscribers to be listening before calling the domain action (auction.join()).

    * Even if we admit that the design in GOOS is subpar (which I don’t believe), you still haven’t proved that mocks are the direct cause. In particular, if we look at the development workflow, which is key in GOOS, you haven’t demonstrated that any characteristic of the “fake it till you make it” outside-in approach based on mocks leads to an intrinsically worse design than another approach. There’s a missing link in the logic here. Just because you deem one mock-driven implementation bad doesn’t mean that all systems that rely on mocks at one stage or another are bad.

    Interestingly enough, I’m using mocks on a day-to-day-basis and end up producing the exact same type of DDD architecture as your proposed “alternative architecture” schema. For instance, I’m mocking repositories and infrastructure services such as email sending to verify that application services call them. These tests are fast, I have very little maintenance work beyond the one you get when you do “regular” tests, and having mocks is certainly not a source of mistakes. I can’t think of a single refactoring that will produce false negatives in tests, instead the code just does not compile most of the time, and it’s a matter of seconds before you make it right.

    To sum it up, you’re mainly blaming the GOOS example for not being a DDD system, which a) doesn’t QED the premise of your article on the question of mocks, b) wasn’t a goal of GOOS anyway and c) only proves partially true if you look closely. Besides, you’re proposing an alternative solution with slower-running tests and where a significant part of the domain richness and expressivity is gone. If we applied to it the same logic as the one you used to review the book, we would say that “the absence of mocks damages the design”. Of course, it is a stupid statement. Mocks are orthogonal to the quality of your design.

    • http://enterprisecraftsmanship.com/ Vladimir Khorikov

      That’s probably the longest comment I saw on this blog so far.

      Alright, so first of all, you are attributing me some assumptions that I didn’t make and didn’t intend to make:

      you’re mainly blaming the GOOS example for not being a DDD system

      I’m definitely not blaming the GOOS example for that. The only reason I bring up DDD terms is because it gives some vocabulary which I can use to express my thoughts. Also, I believe there are universal guidelines to design that are applicable regardless of whether you call them “DDD” or not.

      As I already wrote in a previous comment, the first test you point your finger at uses mocks for a reason. It’s because the tested interaction crosses boundaries.

      It does. However, my point is that it shouldn’t. AuctionSniper and other classes are better off being fully isolated, i.e. not communicating with the outside world directly. Not all interfaces are introduced because of that reason, though. Look at SniperCollection.

      You don’t verify that specific interaction other than through end-to-end tests that run against the UI (so one particular implementation of a listener), thus much slower and carrying the extra complexity of a UI library dependency.

      If you are going to have end-to-end tests anyway, what is the point in duplicating them with additional tests with mocks? In my opinion, it’s a waste of time. Now, if you are not intending to have end-to-end tests, that’s a different matter. In this case, some sort of a test double (mock, spy, etc.) would be required to verify that the view model talks to the chat correctly. As I mentioned in this and in the previous posts, mocks are justified when you are talking with the external world. Just not in this particular case.

      You make AuctionSniperViewModel (the UI) get and decypher an XMPP auction message string directly by using AuctionEvent.From() which is a total nonsense in a DDD setting […]

      I disagree with this one too. First, AuctionSniperViewModel is not UI, it’s part of Application Services – an intermediate layer between the domain layer and the outside world (but I’m being a grammar nazi here). The central source of truth here is exactly the domain model, and AuctionEven is part of it. It contains the logic of translating and validating events coming from the server. It’s not a “domain helper” as you called it.

      You’re basically doing Chat > UI > Domain “helper” instead of the original Chat > Translator > AuctionSniper > UI which both didn’t short-circuit the domain and made the concept of translator explicit (it is not the only rich business concept you lost in the process by the way – see Portfolio)

      Not sure I understand you argument regarding explicitness. What can be more explicit than a method that transforms a string into a domain concept, event in this case? Also, I haven’t lost Portfolio (along with other classes, such as Item), I deliberately cut them out. They are not needed to solve the problem the book posed, thus, they should be removed due to YAGNI.

      Another incoherence between the code and your line of thought is that in another place you do use a kind of mock – FakeAuction […] you could perfectly replace it with a library-provided mock

      There’s a big difference between a library-provided mock and this fake. The former just verifies that your code calls some methods that you yourself defined, and that’s it. The fake works with the real auction server and sends it real messages. The amount of real code that get exercised here is much larger than it would be should you use a mock, and thus the protection against regression bugs is also much better. The authors put a lot of emphasis on building as “end-to-end” tests as possible, and I agree with them on that subject. The more layers the end-to-end tests traverse, the more potential issues they reveal. Just because the fake also has methods like HasReceivedMessage, doesn’t mean you could replace it with a mock and get the same value out.

      Even if we admit that the design in GOOS is subpar (which I don’t believe), you still haven’t proved that mocks are the direct cause. […] Just because you deem one mock-driven implementation bad doesn’t mean that all systems that rely on mocks at one stage or another are bad.

      I agree with that. There are very few things you can mathematically prove in the world of software development anyway, so the only tool we have at our disposal is providing examples with a hope to convenience someone. At the same time, I do think that the original design of the sample project is inferior because of the reasons I stated in the article, and I used it as an example.

      Interestingly enough, I’m using mocks on a day-to-day-basis and end up producing the exact same type of DDD architecture as your proposed “alternative architecture” schema

      That’s indeed interesting. You’ve spent quite a lot of time defending the original architecture from the book while it seems that you wouldn’t implement it that way either.

      These tests are fast, I have very little maintenance work beyond the one you get when you do “regular” tests, and having mocks is certainly not a source of mistakes. I can’t think of a single refactoring that will produce false negatives in tests, instead the code just does not compile most of the time, and it’s a matter of seconds before you make it right.

      When using mocks for substituting external services, you don’t get many false positives, indeed. Only when you do that for mocking the internals of a domain model, like the book did. But you miss another important attribute that in my opinion comprises a valuable test suite. With mocks, your tests don’t traverse much of the real code and thus the amount of potential issues they can reveal is quite low. On the contrary, with end-to-end or integration tests that touch some of the external dependencies, the protection is better. It is a trade-off of course, and you do lose speed, but I would trade some speed for better protection on any given day. When talking about integration tests that don’t work with UI, the speed degradation is not too substantial.

  • Andrzej

    Hi, from where did you take the term “head interface”. I cannot find it anywhere. I know it as “Impl class antypattern”. Even Fowler discouraged that.

    • Robert Pająk
    • http://enterprisecraftsmanship.com/ Vladimir Khorikov

      I just realized that I used this term incorrectly. As Robert noted, it should be “header interface”, not “head interface”.

  • pschwarz

    Hello,

    you said: “The very first thing that meets the eye is lots of header interfaces. That are interfaces that fully mimic a single class that implements them. For example, the XMPPAuction class has a 1-to-1 correspondence to the Auction interface, AcutionSniper – to AuctionEventListener, and so on. Interfaces with the only implementation don’t represent an abstraction and generally considered a design smell.”

    So I presume for you the following passages from GOOS don’t justify interfaces with a single implementation:

    “Identifying Relationships with Interfaces (GOOS p63): We use Java
    interfaces more liberally than some other developers. This reflects
    our emphasis on the relationships between objects, as defined by their
    communication protocols. We use interfaces to name the roles that
    objects can play and to describe the messages they’ll accept.”

    “Budding Off: Defining a New Service That an Object Needs and Adding a New Object to Provide It (GOOS p61) When the code is more stable and
    has some degree of structure, we often discover new types by “pulling”
    them into existence. We might be adding behavior to an object and find
    that, following our design principles, some new feature doesn’t belong
    inside it. Our response is to create an interface to define the
    service that the object needs from the object’s point of view. We
    write tests for the new behavior as if the service already exists,
    using mock objects to help describe the relationship between the
    target object and its new collaborator; …The development cycle goes
    like this. When implementing an object, we discover that it needs a
    service to be provided by another object. We give the new service a
    name and mock it out in the client object’s unit tests, to clarify the
    relationship between the two. Then we write an object to provide that
    service and, in doing so, discover what services that object needs. We
    follow this chain (or perhaps a directed graph) of collaborator
    relationships until we connect up to existing objects, either our own
    or from a third-party API. …We think of this as “on-demand” design:
    we “pull” interfaces and their implementations into existence from the
    needs of the client, rather than “pushing” out the features that we
    think a class should provide.

    Philip

    • http://enterprisecraftsmanship.com/ Vladimir Khorikov

      Hello Philip.

      That’s correct. It might be fine to create interfaces with a single implementation temporarily if you want to adhere to the top-down TDD. But I’d recommend removing them after the actual implementation is formed.

  • Rodi de Boer

    “The very first thing that meets the eye is lots of header interfaces. That are interfaces that fully mimic a single class that implements them. For example, the XMPPAuction class has a 1-to-1 correspondence to the Auction interface, AcutionSniper – to AuctionEventListener, and so on. Interfaces with the only implementation don’t represent an abstraction and generally considered a design smell.”

    Actually, those are not necessarily header interfaces. They needed an Auction class as a collaborator and designed it from the need of the client. Then they implemented a specific implementation of that interface with XMPP in mind. Actually it decouples it from the specific technical implementation.

    AuctionSniper does not only implement AuctionEventListener, but Listening to Auction Events is a “role” of theAuctionSniper. Not sure if it has more roles, but it probably does.

    Interfaces with only one implementation could be a smell, but certainly aren’t by definition. I think there is a misunderstanding about header interfaces compared to role interfaces. Reading the follow ups to the mentioned post from Mark Seemann might be good too: http://blog.ploeh.dk/2010/12/03/Towardsbetterabstractions/ and http://blog.ploeh.dk/2010/12/18/OnRoleInterfaces,theReusedAbstractionsPrincipleandServiceLocators/. Also Martin Fowler has some interesting points about these two: http://martinfowler.com/bliki/RoleInterface.html

    The design that is represented here, without any interfaces, looks like it violates the Open/Closed Principle as there is not really a way to add or alter features without opening the objects. Adding decorators for example is really easy with the use of role interfaces. What about Null Objects? They might come in handy and are easily implemented with the same role interfaces.

    The design might seem (or even be) pretty good, but I am curious how this design came to be. Did it grow with each requirement/story/item that got implemented, leaving a “working” and deliverable product along the way, discovering new objects and so on along the way, or was it constructed with the end product in mind, knowing all the tricky bits and pieces in advance. And yes, this is a relatively small project/product for the sake of being able to pack the whole workflow in a book, while keeping it big and complex enough to get some interesting design and technical decisions in there too. And how do you get from here to the future, with new requirements getting added to the list? (I’m just curious how the design has grown to this point and how it would grow onwards, without trying to be sarcastic, although after rereading this, it might come across as such)

    • http://enterprisecraftsmanship.com/ Vladimir Khorikov

      Thank you for your thoughtful comment.

      AuctionSniper does not only implement AuctionEventListener, but Listening to Auction Events is a “role” of theAuctionSniper. Not sure if it has more roles, but it probably does.

      It doesn’t. Here’s the full diagram of the original architecture: https://i.imgur.com/gLnbFGt.png

      That’s the problem with the implementation from the book. It gives an impression that interfaces in it are all related to some well-defined abstraction while in fact they are not.

      I don’t think they are role interfaces either. Role interfaces, by definition, represent a single operation a class implementing that interface can perform. While in the book’s implementation, most of the interfaces have multiple members. For example:

      public interface Auction {
      void join();
      void bid(int amount);
      void addAuctionEventListener(AuctionEventListener listener);
      }

      I would give the original implementation the benefit of the doubt if the interfaces in it were indeed one member interfaces. But they are not, and that makes them clear header interfaces, in my opinion.

      The design that is represented here, without any interfaces, looks like it violates the Open/Closed Principle as there is not really a way to add or alter features without opening the objects. Adding decorators for example is really easy with the use of role interfaces.

      There are two definitions of OCP. It looks that you use the Bob Martin’s one. This kind of OCP is good when you know the use cases ahead of time and can lay extension points appropriately. Without the actual requirements at hand, however, such arrangements made in advance tend to be premature and lead to overcomplicated solutions. Here I wrote more on this: http://enterprisecraftsmanship.com/2015/06/11/yagni-revisited/

      Did it grow with each requirement/story/item that got implemented, leaving a “working” and deliverable product along the way, discovering new objects and so on along the way, or was it constructed with the end product in mind, knowing all the tricky bits and pieces in advance

      The alternative version was definitely implemented having the end goal in mind because I already had the original code base before me. However, I’m sure I would come up with a similar implementation should I do that exercise by my own. The difference basically boils down to top-down vs bottom-up approach to building software.

      And how do you get from here to the future, with new requirements getting added to the list?

      I would too write an end-to-end/integration test first but then would switch to the bottom-up approach and grow the software from there to the point where it satisfies the test. If you are interested in the process itself, I would recommend my DDD in Practice Pluralsight course: https://www.pluralsight.com/courses/domain-driven-design-in-practice While the main focus of it is DDD, it explains the topic using a fully-fledged example grown from the the ground up, with TDD and changing requirements along the way.

      • Rodi de Boer

        Thank you. I think we have different opinions about some things, but you make some interesting points. I will certainly learn from this and broaden my knowledge.
        My Pluralsight membership has ended some time ago and not sure if I will renew, but I will save the course for when I do.
        One thing I would like to mention is that a Role Interface does not *have* to be a single member interface. The naming of the interface communicates the role and its members should be part of that role. I think GOOS did a pretty good job on that one. But like you mentioned it might all come down to the difference between top-down and bottom-up.

        • http://enterprisecraftsmanship.com/ Vladimir Khorikov

          You are probably right that a role interface doesn’t have to be a single member interface, although I can’t remember any examples backing that suggestion off the top of my head (and also interfaces from the articles you referenced did have a single member in them). But then again I would question the role of this method for example (highlighted):

          public interface Auction {
          void join();
          void bid(int amount);
          void addAuctionEventListener(AuctionEventListener listener);
          }

          I don’t think it has any meaningful role aside from a pure technical one.

  • Peter Van Wick

    In this example the enums make sense because we need to de-serialize messages to AuctionEvent and serialize commands to AuctionCommand. In general though we don’t always need serialization to interact with external services: the library that’s interacting with the auction service might handle that for us, by providing callbacks for auction events and methods for the different auction commands.

    If this were the case, it feels like a code smell to return an AuctionCommand rather than call a method. When using Commands rather than method calls you lose type safety and type information (which is invaluable when you want to use your IDE’s refactoring tools). But at the same time, we don’t want the AuctionSniper to depend on the class that talks to the auction server. Is there a way around this?

    • http://enterprisecraftsmanship.com/ Vladimir Khorikov

      The general guideline here is to separate the decisions the domain model makes from the effects of those decisions, and then verify the former. In the alternative implementation, they are pretty close to each other (the content of the command is the decision and the string sent to the auction server is the effect), but they still don’t correspond to each other as 1-to-1. For example, if AuctionSniper decides not to take any action, it still returns a command, but we don’t send those commands to the server.

      Now, if the action server’s API was different and instead of a single method had separate ones for each kind of the commands, everything would remain the same except you’d need to introduce a mapper that would convert the commands into calls on the library. So basically the logic which is currently wired into the AuctionCommand.ToString() method would go to that mapper. It can look something like this:


      class MyTranslator
      {
      private XmppChat _chat;

      public MyTranslator(XmppChat chat)
      {
      _chat = chat;
      }

      public void Translate(AuctionCommand command)
      {
      if (command.Type == CommmandType.Bid)
      {
      _chat.Bid(...)
      }
      ...
      }
      }
      You would then be able to unit test this class in isolation from other classes. Note that here, you do need to use mocks to do that. The AuctionSniperViewModel class would remain mostly the same, as well as the domain model would still remain isolated.

      • Peter Van Wick

        I agree with everything you’re saying and I definitely see the benefit of isolating the domain from its communication with the outside world. What I’m saying is passing command objects around is much less expressive than method calls. You lose a lot of things that we usually take for granted. Your compiler won’t complain if you pass a command object that the translator hasn’t implemented. If an exception is raised the stack traces won’t specify which command was passed to the translator. If there are a bunch of different methods with different parameters you’ll have to manually add validations that the command object has all the required parameters according to the type of command. I think the benefits of isolating the domain outweigh these drawbacks but I’m wondering if there’s another way to isolate the model that doesn’t have this problem.

        • http://enterprisecraftsmanship.com/ Vladimir Khorikov

          I’m wondering if there’s another way to isolate the model that doesn’t have this problem

          I don’t think there are other ways, at least I’m not aware of them. Let me know if you find any.

          Interesting points regarding the drawbacks.

          Your compiler won’t complain if you pass a command object that the translator hasn’t implemented.

          I don’t think this problem is specific to the use of translator, though. We could have the same issue should we forget to implement the corresponding functionality in the AuctionSniper class itself (in case we attribute the responsibility to call the external library to this class).

  • Mauricio

    Hi Vladimir,

    I guess you summarize a lot of my feelings while reading the Goos book: is a great intro to TDD and ports and adapters, but lacks some “isolation” on the testing part.

    However, let’s says that 2 new requirements came up:

    1. Storage: The messages should be stored by the sniper to keep a trace. My initial thinking would be to make the application service (AuctionSniperViewModel) take care of that responsibility, in that case the logic will still be isolated from the infrastructure, however:

    2. Another client: Let’s says there’s a new command line client that should use the same database (I know in your database delivery best practice’s Pluralsight course you advise against this integration database approach, but I’m trying to constraint the scenario to a possible on in real life). In this case, I would extract the application layer out of the UI project, would create a new project to hold the application layer (DRY) and use in both clients the application layer.

    That would be my naive implementation, however I would like to know what could be your solution.

    Thanks for the information you make available to all of us.

    Mauricio

    • http://enterprisecraftsmanship.com/ Vladimir Khorikov

      Hi Mauricio,

      There’s actually nothing wrong in the approach you propose in #2 as long as the new client is just that – a client, and shares all logic with the main application besides the UI layer. I wouldn’t consider the command line application a separate system in this case, and instead would treat it as part of the same bounded context which means that it can share the same database with the rich UI app. It only becomes a problem to do so when the application is not part of the same bounded context (i.e. solves a business problem other than that of the “normal” project). Note also that all apps in a bounded context should be developed by the same team. Sharing business logic between teams is harmful (just as it’s harmful to share it between bounded contexts).

      So, your approach is correct. Extract a separate application services layer out of the WPF UI, re-use it in the new console app, make it store processed commands for traceability purposes. And also: treat the new app as part of the same BC, and make sure it is developed by the same team.

      • Mauricio

        Thanks you Vladimir, after sinking the answer in my head, I guess I have it clear now. My next step would be to try to add functionality to your alternative implementation, to check if my understanding is correct.

        Thanks again, and looking forward to the next Pluralsight course!!, any comments on that?

        • http://enterprisecraftsmanship.com/ Vladimir Khorikov

          Great! Feel free to share your findings with me.

          Regarding my next Pluralsight course, I’m actually working on a small one right now, it’s about the specification pattern in C#. And after that, I was thinking to do a course about refactoring from anemic domain model to a rich one. This one should be fun 🙂