Sometimes talking through a problem with a rubber duck can yield surprising insights.
With increasing frequency, I’ve been hearing comments to the effect that when people refactor code, they break existing test cases that were passing before they refactored.
When I ask whether the test cases “know” about underlying implementation details in the code under test, they look puzzled. Well, of course. How else would you know the code was working?
Hmm.
So, when I ask them whether they test-drive the refactorings, they look puzzled. Well, of course. We always test drive code changes.
Hmm.
So, when I ask them whether the tests break due to refactoring, even when the refactorings are test-driven, they look puzzled. Well, of course. That’s what we said, isn’t it?
Hmm.
I didn’t see how the last two statements could be true at the same time. I needed advice from my rubber duck.
Me. Hey, duck!
R. Duck. Oh, you talking to me? After all these years gathering dust on the shelf, all I get is a “Hey, duck?” Typical. Just…typical.
Me. Sorry. So, Mr Duck, I was hoping you might help me with my confusion.
R. Duck. Sure. You want to get confused or unconfused?
Me. Um…unconfused, please.
R. Duck. Okay. What are we talking about, then?
Me. What’s time?
R. Duck. It’s six forty-three.
Me. No, not what’s the time. What is time?
R. Duck. I see. Can you be more specific?
Me. More specific. Hmm. Okay. Is time linear?
R. Duck. Is that all?
Me. That’s all, yeah.
R. Duck. Well, I can tell you all that time sitting on the shelf gathering dust sure felt linear to me.
Me. Back to that again, are we? I said sorry.
R. Duck. You said it, but only Canadians really mean it.
Me. If you say so. So, if the question is so easy, what’s the answer?
R. Duck. The answer is “yes,” time is linear.
Me. You’re sure about that?
R. Duck. Pretty sure. Why? Has something happened to make you doubt it?
Me. Yes.
R. Duck. Intriguing. Please continue.
Me. Well, a growing number of software professionals whom I respect tell me that when they refactor production code, it breaks test cases.
R. Duck. That’s not unusual.
Me. So I gather.
R. Duck. It means they have test cases that know too much about the underlying implementation details of the code under test.
Me. That’s what I thought, too.
R. Duck. So what’s the confusion?
Me. They insist their test cases need to have that knowledge in order to check the code.
R. Duck. Well, I doubt it, but I haven’t seen their code, so maybe they’re right.
Me. That’s what I thought, too.
R. Duck. If you’re just going to keep saying, “that’s what I thought, too”, what do you need me for?
Me. To bounce ideas off of. Because you’re–
R. Duck. Don’t say it!
Me. –rubber.
R. Duck. <sigh> So that’s how it’s going to be. Anyway, if they have test cases like that, then they probably should test-drive the refactoring.
Me. That’s what I– Um, that is, when I asked about that, they said they do test-drive the refactoring.
R. Duck. And it still breaks tests cases?
Me. Apparently.
R. Duck. And they start by changing the test cases to express the implementation details they plan to arrive at through refactoring?
Me. Well, they say they test-drive the refactorings. That’s what it means, isn’t it?
R. Duck. In my book. But what do I know? I’m just a rubber duck.
Me. So, you can see how the question of the linearity of time comes up.
R. Duck. Sure, I see that. But, um…why don’t you tell me anyway, just to be sure you understand.
Me. Okay. It’s like this, see. Assume time is linear.
R. Duck. Always have.
Me. Me too. Now, say we have two events – call them Event A and Event B. If Event A occurs first, and is completed before Event B starts, then it’s possible Event A could have an effect on Event B.
R. Duck. Clearly.
Me. But it’s not possible for Event B to have an effect on Event A.
R. Duck. I wouldn’t think so, no. Event A is a done deal before Event B happens.
Me. That’s what I thought, too. Oops. Sorry.
R. Duck. So, you’re saying if someone test-drives a refactoring, Event A is to change the test case, and Event B is to refactor the production code. If Event A happens before Event B, then…
Me. …then it’s impossible for the refactoring to break the test case!
R. Duck. Okay, look. I was talking. That was Event A. Then you interrupted me. In linear time, you’re supposed to wait for the other person to finish before you start talking. That’s Event B.
Me. Right. Sorry. But that’s actually a good example of my confusion.
R. Duck. I’m tempted to comment on your confusion, but that would be too easy.
Me. What?
R. Duck. Nothing. Please continue.
Me. These colleagues of mine…they’re highly skilled professionals and ethical individuals who speak honestly.
R. Duck. Sounds like you’re in fine company.
Me. I am. And that’s the thing, see. They must be telling the truth, because they’re honest. That means they must be test-driving these refactorings. And yet, the refactorings – Event B – are having an effect on the modified test cases – Event A. The only logical conclusion is that time itself is non-linear.
R. Duck. You may be onto something, there.
Me. You really think so?
R. Duck. No. But please continue.
Me. <with mounting urgency> If it’s possible for test-driven code changes to break the test cases that drove them, then time is non-linear. Even if the developer modifies the test cases first, those changes may occur before or after the subsequent changes to the production code, or the changes to both could overlap. There’s no way to ensure Event A happens before Event B. Future changes to the production code go backward in time to break test cases before they were set up to “drive” the changes.
R. Duck. The practical implications of which are…?
Me. <excitedly> Everything that could possibly happen is happening right now, all at the same time, right here!
[There ensued a long pause during which nothing appeared to happen.]
R. Duck. Or maybe they just mis-spoke about test-driving the refactoring.
Me. <dully> Quantum entanglement?
R. Duck. Don’t overthink it. They didn’t test-drive, that’s all.
Me. That’s anti-climactic.
R. Duck. Why?
Me. The other thing sounded a lot more exciting.
R. Duck. True, that.
[There ensued a second long pause, as uneventful as the one that had occurred previously in linear time.]
R. Duck. Pizza?
Me. Sounds good, yeah.