Behaviour vs. State

Luke Phyall
Shoreditch Warlock
Published in
5 min readApr 14, 2019

I’ll be frank at the commencement: something happened this week that blind-sided my cohort, and it’s caused a hell of a stir.

This post is part of my ongoing attempts to deal with said subject, and, as such, is a work in progress. It may well turn out to be wrong, and I’m quite possibly going to change my mind. With that said, I hope something in here triggers something for you if you’re confronting the same issue.

As stated in my previous post, this week at Makers was individual tech tests, inserted into the curriculum as preparation for what we’re about to face. For me, it came about in a slight X-Files flavour. I was attempting the Bank test, which simply is a simulation of a basic bank interface: your code should allow you to withdraw money from a fiction bank account, deposit funds, and show you a correctly-formatted statement on demand. After a total false start, I hacked my original modelling to pieces with a conceptual axe and started again; in the first iteration, I’d decided to store the fictional balance inside its own class. After one day of coding, it was clear to me that this needed to be binned in favour of using an instance variable.

However, following a principle that’d been introduced to us months ago (that you should at all time only expose the barest of your code necessary), I hit a wall. In order to test that my withdraw and deposit methods were doing something to the balance variable, a simple Equal matcher wouldn’t work unless I exposed the balance. I didn’t want to expose the balance.

Just in the same way that you don’t go to the bank and take all your money out just to go shopping for milk and sausages, it made absolutely no sense to expose the balance variable just for the sake of being able to poke it with an Rspec stick. When test driving software development, your tests should do exactly that: drive code. Not dictate it.

I wanted to keep the balance buried. The only time you’d need to see it is on a statement, at which point it’s characters on the screen. When you visit a cash machine and request your balance, a little window doesn’t open to let you look directly at your money. You get a report. In need of fire support, I put my thinking to one of the senior members (experience-wise) of my cohort. Was it a sufficient test of the deposit and withdraw methods to check the statement for the presence of the correct amount following the transaction? Could I in fact avoid test the balance variable directly entirely?

She agreed. I carried on.

Later that day, our head coach out of the blue posted an article to us on Slack addressing this exact point. This exact damn point. Entitled “Testing behaviour rather than state, and why you should care”, it made the case with a frighteningly-similar example to the stuff I was doing that you should not EVER test your software’s state. Choosing not to call Mulder I took this as eerie vindication that my colleague and I had hit the nail on the head, felt like a legend, and pressed on.

Asking around over the rest of the week I learned that a number of the cohort had been brought up directly on this behaviour vs state trap, which had caused a degree of vexation. We’d not quite been drilled on the necessity of this and had been merrily checking state pretty much the entire course; only now had it suddenly become a major issue.

How to make the mental switch, then? How to get your head out of the State hole and into the Behaviour sunlight? My current thinking is this: make use of two specific Rspec matchers (or possibly, types of matcher) and bin any use of Equal. The first matcher is Expect:

expect(object).to receive (:method)

Using the London style of testing, we want to check behaviour. If you’ve coded a Waiter class in a Restaurant, the behaviour of that waiter is that either takes your order or brings you something. For testing purposes it doesn’t really matter if it’s bringing you a beer or the bill; what matters is that after you’ve ordered a burger with two extra patties and eight pallets of chips it doesn’t find out where you live and burn your house down. Your order needs to make its way to the kitchen. Using the above matcher, you might do something like the following pseudo-code:

before { expect(chef)to receive(:order).with("Giant burger) }

waiter.order("Giant burger")

Behaviour. Not state. We’re not checking to see if the Waiter’s order array now contains “Giant burger”. That implicitly has to have made its way in there for the Chef to be able to receive it.

What if we absolutely want to make sure that a variable has changed, though? This one occurred to me about an hour ago. Rspec has a Change matcher:

expect { do.something }.to change { object.attribute }

Look at it: you don’t have to check what the object.attribute now is. Rspec will quite happily tell test that a change has occurred. If you’re not bother what that change actually is, neither is Rspec. Behaviour, not state. For a unit test, this is absolutely fine. If you’re determined to see whether the thing that you typed changes to what you expected it to, that needs to go in a feature test.

Obviously, this will only work if the attribute is exposed. Can’t make the test work without exposing it? It’s a bad test. Bin it.

As as patch for those that are struggling with this behaviour vs. state issue right now, try this as an exercise: for your next piece of test-driven code, ONLY use the above two matchers. Maybe allow yourself to check for stdout as a necessary exception to the rule (you’ll need to look that matcher up if you haven’t already), but bin the Equal matcher for the duration.

CLARIFICATION: I’m not saying never use the Equal matcher. The Matcher Police are not going to kick your door down if you decide to employ it. As long as you understand this difference between behaviour and state, you can discard this drill and do whatever you want. The exercise is there to to force you to start considering how to test what your code is actually doing, rather than what it currently is.

--

--

Luke Phyall
Shoreditch Warlock

Junior dev currently training at Makers Academy in London.