DEV Community

Geraldo dos Santos
Geraldo dos Santos

Posted on

A test to remember

The team I work in once came across a very interesting material about unit tests. How to do it in a more relevant way, testing what matters.

While it was interesting, something else caught my attention. The speaker pointed at test names. He brought to light some conventions followed to this very day, namely the combination of Should, Given, When, and Then.

Take a look at this test example and the conventions behind it, I want to test the Withdraw method from my BankAccount class.

So...

  • Given my bank account is in credit, and I made no withdrawals recently
  • When I attempt to withdraw an amount less than my card's limit
  • Then the withdrawal should complete without errors or warnings

Ok. Keep that in mind.

You are a new engineer at the team, you will be working in the BankAccount class for the next couple of days. Andrew, a guy who started a couple of months before you, is showing you the ropes. You're taking a lot of notes to make sure you get the details.

Suddenly, Andrew needs to attend a meeting and leaves you looking at the editor. He asks you to keep playing with the code, looking at the architecture, and to write down questions you might have for when he's back.

You get particularly curious about the Withdraw method in the class you're looking at. You feel you kinda get it, but not quite. Head scratches and frowning keep intensifying.

Eventually you find a unit test for it, and this is what you see.

public void Given_BankAccountInCredit_When_WithdrawIsMade_Then_ShouldSucceed()

While you're at it, you heard that one of your colleagues did well in a Magic the Gathering tournament during the weekend, and because so, she decided to bring cake to the office today.

That was Tina, she's one of the engineers working in the BankAccount class.

You go to Tina after getting one (or more, I'm not judging) slice(s) of cake, then you ask her:

"Hey Tina, thanks for the cake btw! Question, I'm not completely sure what the Withdraw method should do in the BankAccount class. Would you be so kind to explain it to me?"

"Sure, dude. Here's how it goes."

"Given my bank account is in credit, and I made no withdrawals recently, when I attempt to withdraw an amount less than my card's limit, then the withdrawal should complete without errors or warnings."

"That's it, any questions?"

"So, it withdraws money."

"Yup."

"Cool, thanks."

Please prescind from how trivial withdrawing money from an account is and bear with me. I believe there is no need in making the code have such noise when explaining what it does. Naming things is hard, I know, but that is no excuse to make it harder on the reader. Conventions are good, they help us establishing order among the chaos. Then comes maturity, and it's ok to experiment with what you learned, go beyond just that's what the rule says.

Imagine reading hundreds, or thousands of tests written like this, filtering through all those words to understand what's going on. Imagine someone new joining the team, and having to go through all those noisy test names.

When having a chance to explain something to someone, you would not deliberately choose the most difficult and convoluted way to make that person understand what you are clarifying. I know that there's a subreddit about explaining things in a easy way, but haven't found one yet about doing the same in a hard way.

If I would create such a test, it would be like this:

public void Withdraw_Removes_Money_From_Account()

No Shoulds, no Givens, Whens, Thens for the sake of the convention. The name says it all using as few words as possible.

Need more specific scenarios? Awesome! Your tests should be specific so your the logic in your code can be generic.

public void Withdraw_Removes_Money_From_Account()
public void Withdraw_Fails_When_Account_Is_Blocked()
public void Withdraw_Fails_When_Amount_Exceeds_Daily_Limit()
public void Withdraw_Fails_When_Amount_Exceeds_Total_Balance()

This is it, the tests say exactly what happens, and the convention I choose to use is write tests readable for humans.

Some dude out there once said this:

Any fool can write code that a computer can understand. Good programmers write code that humans can understand.

Do you concur?
How do you and/or your team write test names?
Want to share more examples?

Thanks for reading, peace!

Discussion (0)