Port/Adapter/Simulator: read-only and write-only dependencies…

February 24, 2016 at 8:11 am

When dealing with many external dependencies, the Port/Adapter/Simulator pattern works great.

But what about dependencies that are read-only – such as consuming an information feed from another system – or write-only – such as sending a notification to a user? Can we use P/A/S in those scenarios?

The answer is a qualified "yes" – we can get many of the advantages of P/A/S, though we will probably have to give up something.

Stock prices

Let’s consider an application where we deal with stock prices. If we were in a read/write scenario, we would write something like this:

interface IStockPriceStore
{
    Decimal Load(StockSymbol stockSymbol);
    void Save(StockSymbol stockSymbol, Decimal price);
}

And the test that we would write would look something like this:

IStockPriceStore stockPriceStore = new StockPriceStoreMemory();
stockPriceStore.Save("MSFT", 55m);

StockProcessor stockProcessor = new StockProcessor(stockPriceStore);
stockProcessor.Process();
Assert(…);

Okay, that’s not perhaps as minimal as I would like for a test, but it’s not terrible.

But what if our stock feed is read-only? That changes our adapter to be something like:

interface IStockPriceSource
{
    Decimal Load(StockSymbol stockSymbol);
}

To make our tests run the same way, we need someplace to put a Save() method. My approach is to put it directly on the simulator (StockPriceSourceMemory) class. There are a few ways I’ve played around with expressing this:

  1. Put it directly on the class and name it Save()
  2. Put it directly on the class and name it SimulateSave()
  3. Define a separate interface (IStockPriceSink?), put it there, an have the simulator implement it.

 

I don’t really like the third option, as we have an interface that will only have one implementation. I haven’t decided between the first two; sometimes I like the division that option #2 gives, sometimes I think it’s too wordy.

This approach lets us write the product tests exactly the same way we would with a read/write store, but we lose a big advantage of P/A/S – the ability to test for similarity between our real and in-memory implementations of the adapter.

In many cases, I don’t think this matters a lot, as read-only stores tend to be fairly straightforward. In some cases, it does matter, and there’s another technique we can use. We write our adapter tests so that they pass against the real adapter, and then, when we run those same tests against the simulator, we pre-populate it by fetching specific data from the real adapter and storing it into the simulator. The data that we move across is enough to verify that the simulator works correctly.

This will result in two different sets of tests; there will be the pure-simulator unit tests, and then the contract verification tests that we run against the live data and the simulator.

It’s not a perfect solution, but it will be pretty close.

Email User

Emailing a user is a good example of a write-only operation. We’ll write the port like this:

interface IUserNotifier
{
    void Notify(User user, Message message);
}

I’m really hoping that you weren’t expecting a IEmailUser port.

In this case, our simulator needs to do a couple of things. It needs to verify that Notify was called with appropriate parameters, and it needs to be able to simulate any error situations that we want to raise with the calling application.

How about something like this:

class UserNotifierSimulator: IUserNotifier
{
    void Notify(User user, Message message) {}

    User User {get; private set;}
    Message Message {get; private set;}
}

To use the simulator, I pass it into a class that needs to perform a notification and then look and the User and Message properties to see whether it performed the notification.

As for the port tests, I suspect that I’m going to write a few around the port validation behavior to define what happens when either user or message is improperly formed.

There are other possible errors – the network could be unreachable, for example – but I’m not sure I’m going to write any of them into the port. I would prefer to hide them underneath, to make the real adapter responsible for recovery.

Message passing architectures…

And, so that’s how I’ve done it, and it’s worked pretty well.

But… remember my earlier comment about the test code I wrote for the stock store? The problem there is that I’m saving something into a store just so that the stock processor can fetch it right out. But there’s an alternative:

If I think of this as in message-passing/pipeline terms, I can express things differently. Something like:

class StockPriceSource
{
    public event EventHandler<StockPrice> StockPriceReady;

    public void FetchStockPrice(StockSymbol stockSymbol);
}

I can then hook this up to a modified version of the StockProcessor class by just hooking a method to an event. This approach means that I don’t really need my simulator any more; I have something that can produce one (or a set of) stock prices, and then things that know how to consume them.

I think this probably makes my world simpler. I can write a few quick contract tests for StockPriceSource to verify that it is working, and I can test all the processing just by passing in data.

You suck at TDD #3 – On Design sensitivity and improvement

February 11, 2016 at 8:31 am

Through an above-average display of ineptness, I managed to overwrite my first version of this post with a response I wrote to the comments. Since I’m too lazy to try to reconstruct it, I’m going to go a different direction. In a recent talk about design, I told the attendees that I was going to insult them during the talk, and it worked pretty well, so I’m going to try the same thing here:

You don’t know how to do design. You would not know a good design if it came up and bit you.

To explain this, I feel it necessary to talk a bit about ignorance. I will loop back to the topic at hand sometime in the future.

In a really cool article written back in 2000, Phillip Armour wrote about 5 orders of ignorance, which I’ll summarize here:

  • Oth Order Ignorance (0OI) – Lack of ignorance, or in other words, knowledge. I have 0OI about skiing.
  • 1st Order Ignorance (1OI) – Known lack of knowledge. I have 1OI about how to knit, but I can think of a number of ways to convert this to 0OI.
  • 2nd order Ignorance (2OI) – Lack of awareness. I have 2OI when I don’t know that I don’t know something. I cannot – and this is the point – give examples here.
  • 3rd order Ignorance (3OI) – Lack of process. I have 3OI when I don’t know a suitably efficient way to find out that I don’t know that I don’t know something.
  • 4th order ignorance (4OI) – Meta ignorance. I don’t know about the 5 orders of ignorance.

 

Like any model, this is a simplification of what is really going on, but it’s a useful model.

Back to the insult, but this time I’ll reword it using the orders of ignorance:

You have 20I about design; the code that you are writing has obvious problems with it but you do not see them because you are 2OI about these problems and therefore cannot see the problems yourself.

It’s a little less catchy, and that’s why this series is named "You suck at TDD", not "Systemic influences and limitations in human knowledge acquisition have limited your effectiveness at TDD in interesting ways".

You can probably think back when you learned something about coding – for example, there was probably a time when you learned it was good not to repeat code. Before you learned that, you had 2OI ignorance about it, and now you no longer do.

We *all* have at least some 2OI about design; it’s just a feature of how knowledge acquisition works. That is not the real problem.

The real problem is that most developers have both 3OI and 4OI when it comes to design. They don’t understand how 2OI works and therefore think that they have good design skills, and – probably more importantly – they don’t have an efficient way of identifying specific instances of 2OI and converting them to 1OI and ultimately 0OI.

Or, to put it more concisely, most developers think they are good at design because they can’t see the problems in their designs, they don’t really understand that this is the case, and they don’t have any plan to improve.

If you couple this with our industry’s typical focus on features, features, features, it is no longer surprising that there is so much bad code out there; it becomes surprising that there is code that works at all.

So, tying this whole thing back to TDD, the reason that TDD does not work for a lot of people is that they don’t have sufficient design skills to see the feedback that TDD is giving to them. They get to the refactor step, shrug, and say, "looks good to me; let’s write the next test". And these 2OI issues add up, and they make it harder to do TDD on the code, and it gets hard to do.

This is not surprising. TDD is about evolutionary design, and if you aren’t very good at design, it’s not going to work very well.

Improving your design skills

The remainder of this series is about improving your design skills. I will probably focus on specific patterns that I find especially useful in the TDD world, but I’d like to provide some general advice here. These are things that will help you move from 2OI to 1OI in specific areas of design. And please, if you have other ideas, add them in the comments:

  1. Pair with somebody who cares about design. They will be 1OI or 0OI in areas where you are 2OI, and by having discussions you will learn together.
  2. Read and study. There is a *ton* of useful information out there. If you like abstract, read – and then re-read Fowler’s book on Refactoring. If you like more specificity, go read information about a single code smells, and then find it – and fix it – in your code. If you want a bigger example, just search for refactoring, and you’ll find examples like this.
  3. Share and teach. You may think that you know something about a specific area of design, but when you go to teach it to others, that is when you really learn it.
  4. Practice refactoring skills. If you are lucky enough to work in a language with good refactoring tools, train yourself to use them instead of hand-editing. Not only will this speed you up and reduce the errors you make, it will train you to think differently about code.
  5. Practice TDD and experiment with different approaches. What does "tell, don’t ask" do to your code? Can a more functional programming approach have benefits?

Agile Open Northwest 2016 recap

February 9, 2016 at 8:13 pm

Last week, I spent three days at Agile Open Northwest 2016, a small (300-ish people) agile conference held at the Exhibition Hall at Seattle Center.

Well, perhaps ‘conference" is the wrong word; one of the first things that I read about the conference said the following:

Agile Open Northwest runs on Open Space Technology (OST).

I am generally both amused and annoyed by the non-ironic appending of the word "technology" to phrases, but in this case, I’m going to cut them some slack, because Open Space really is different.

I’ve done a lot of conferences in my time, and one of the problems with conferences is that they are never really about the things that you want them to be about. So, you pull out the conference program, try to figure out what the talks are really going to be like, and go from there.

Open space technology is basically a solution to that problem. It’s a self-organizing approach where a minimal bit of structure is put into place, and then the conference is put on by the attendees.

So, somebody like me could host a session on TDD and design principles and #NoMocks, and – in collaboration with a few others (including Arlo) – we could have very fun and constructive session.

And – far more cool in my book – somebody who knew just a little about value stream mapping to could host a session on applying value stream mapping to software projects and have the people who showed up teach me.

The other non-traditional thing with the conference is the law of personal mobility, which says that it’s okay – no, it’s required – that if you aren’t learning or contributing at a session you have chosen, you leave and find a better use of their time. Which means that people will circulate in and out of the sessions

With the exception of one session, I enjoyed and learned something at all of the sessions that I went to.

The one downside of this format is that you need engaged people to make it work; if you take a bunch of disinterested people and ask them to come up with sessions, nobody is going to step up.

I also got to do a couple of Lean Coffee sessions at breakfast Thursday and Friday. These are a great way to get ideas quickly and cover a lot of topics in a short amount of time.

Overall, I had a great time. If you have passion around this area, I highly recommend this conference.