Exploration Through Example

Example-driven development, Agile testing, context-driven testing, Agile programming, Ruby, and other things of interest to Brian Marick
191.8 167.2 186.2 183.6 184.0 183.2 184.6

Sun, 24 Sep 2006

Describing yes/no choices in Fit

Using Fit to describe boolean (yes/no) decisions can be much clearer if you just insist that all decisions be expressed in multiple, uniform, simple tables. No boolean expressions in the code may mix ands and ors, but that's not a bad idea anyway in this age of small methods and ubiquitous languages.

Suppose you're given a jumble of three packs of cards. You are to pick out every red numbered card that's a prime, not rumpled, and is from either the Bicycle pack or the Bingo pack (but not from the Zed pack). Here is a way you could write a test for that using CalculateFixture:

which pack? color? prime? rumpled?   select?
Bicycle red 3 no   yes
Bingo red 3 no   yes
Zed red 3 no   no
Bingo black 3 no   no
Bingo red 4 no   no
Bingo red Queen no   no
Bingo red Ace no   no
Bingo red 3 yes   no

I bet you skimmed over that, read at most a few lines. The problem is that the detail needed to be an executable test fights with the need to show what's important. This is better:

which pack? color? prime? rumpled?   select?
Bicycle red 3 no   yes
Bingo red 3 no   yes
Zed red 3 no   no
Bingo black 3 no   no
Bingo red 4 no   no
Bingo red Queen no   no
Bingo red Ace no   no
Bingo red 3 yes   no

That highlights what's important: any card must successfully pass a series of checks before it is accepted. This test better matches what you'd do by hand. Suppose the cards were face down. I'd probably first check if it were rumpled. If so, I'd toss it out. Then I'd probably check the back of the card to see if it had one of the right logos, flip it over, check if it's black or a face card (two easy, fast checks), then more laboriously check if it matches one of the prime numbers between 2 and 10 (discarding Aces at that point).

The code would be slightly different because it has different perceptual apparatus, but still pretty much the same:

return false if card.rumpled?
return false if card.maker == 'Zed'
return false if card.color == 'black'
return false if ['2', '3', '5', '7'].include?(card.value)
return true

It does bug me that the table looks so much more complex than the code it describes. It still contains a lot of words that don't matter to either the programmer or someone trying to understand what the program is to do. How about this?

All the following must be true to accept a card:
description example counterexample
the right manufacturer Bicycle, Bingo Zed
the right color red black
the number is prime 2, 3, 5, 7 4, Ace, Queen, etc.
the card is unrumpled yes no

From this, the Fit fixture could generate a complete table of all the given possibilities, run that, and report on it. (Side note: why did I pick Queen as a counterexample instead of Jack or King? Because if the program is storing all cards by number, the Queen will be card 11. Since I'm not going to show all non-primes—believing that more trouble than it's worth—I should pick the best non-primes.)

The same sort of table could be created for cases where any one of a list of conditions must be true.

Now, many conditions are more complicated than all of or none of or any one of. However, all conditions can be converted into one of those forms. Here's an example.

Suppose you're allowed to pay a bill from an account if it has enough money and either the account or the "account view" allows outbound transfers. That would be code like this:

class Account
  def can_pay?(amount)
    balance >= amount && (self.may_transfer? or view.may_transfer?)
  end

However, that could also be written like this:

class Account
  def can_pay?(amount)
    balance > amount && is_money_source?
  end

  def is_money_source?
    self.may_transfer? or view.may_transfer?
  end

I claim that code is just as good or even better. It's better because there's less of a chance of a typo leading to a bug (writing a && b || c instead of a && (b || c)). It's also arguably better because a new word and perhaps idea have been introduced into the project language: "money source". I think finding the right words is often important.

The corresponding tables would be like this:

All of the following are required to pay a bill:
the balance must be sufficient
the account must be a money source

One of the following is required to be a money source
the account may transfer
the accounts view may transfer

In this particular case, I left off the Example and Counterexample columns because they're obvious. I'd expect the fixture to fill them in form me. I didn't include a table about the balance being correct because I wouldn't think the programmers would need it, nor would others need it to believe the programmers understand it.

One thing that worries me about this is that the table doesn't rub your nose in combinations. Such a table is more likely to force you to discover business rules you'd forgotten about, that you'd never known about, or that no one ever knew about. (Well, it does that for a while - until the tedium makes your mind glaze over.) In a way, this fixture makes things too easy.

On the other hand, there's something to be said for protecting later readers from the process through which you convinced yourself you understood the problem.

I'm tempted to launch into implementing this, but I have other things to work on first.

## Posted at 11:49 in category /fit [permalink] [top]

About Brian Marick
I consult mainly on Agile software development, with a special focus on how testing fits in.

Contact me here: marick@exampler.com.

 

Syndication

 

Agile Testing Directions
Introduction
Tests and examples
Technology-facing programmer support
Business-facing team support
Business-facing product critiques
Technology-facing product critiques
Testers on agile projects
Postscript

Permalink to this list

 

Working your way out of the automated GUI testing tarpit
  1. Three ways of writing the same test
  2. A test should deduce its setup path
  3. Convert the suite one failure at a time
  4. You should be able to get to any page in one step
  5. Extract fast tests about single pages
  6. Link checking without clicking on links
  7. Workflow tests remain GUI tests
Permalink to this list

 

Design-Driven Test-Driven Design
Creating a test
Making it (barely) run
Views and presenters appear
Hooking up the real GUI

 

Popular Articles
A roadmap for testing on an agile project: When consulting on testing in Agile projects, I like to call this plan "what I'm biased toward."

Tacit knowledge: Experts often have no theory of their work. They simply perform skillfully.

Process and personality: Every article on methodology implicitly begins "Let's talk about me."

 

Related Weblogs

Wayne Allen
James Bach
Laurent Bossavit
William Caputo
Mike Clark
Rachel Davies
Esther Derby
Michael Feathers
Developer Testing
Chad Fowler
Martin Fowler
Alan Francis
Elisabeth Hendrickson
Grig Gheorghiu
Andy Hunt
Ben Hyde
Ron Jeffries
Jonathan Kohl
Dave Liebreich
Jeff Patton
Bret Pettichord
Hiring Johanna Rothman
Managing Johanna Rothman
Kevin Rutherford
Christian Sepulveda
James Shore
Jeff Sutherland
Pragmatic Dave Thomas
Glenn Vanderburg
Greg Vaughn
Eugene Wallingford
Jim Weirich

 

Where to Find Me


Software Practice Advancement

 

Archives
All of 2006
All of 2005
All of 2004
All of 2003

 

Join!

Agile Alliance Logo