Exploring Solution Spaces © Copyright 2003-2006, by C. Keith Ray
   


About
Exploring Solution Spaces, Keith Ray's blog on Software development and other topics.

Send comments to:
keithray@mac.com

For Agile Training, eLearning, or Coaching contact:
Industrial Logic, Inc.
866-540-8336 (toll free)
510-540-8336 (Berkeley, California)

Links
xpminifaq
Résumé
“Adopting XP” Article 2002 (pdf)
“ Refactoring” Article 2006
AYE Conference
Lucien W. Dupont
Elisabeth Hendrickson
Johanna Rothman's Managing Product Development
Brian Marick's Exploration Through Example
Esther Derby's Insights You Can Use
Laurent Bossavit's Incipient(thoughts)
Dale Emery's Conversations with Dale
Martin Fowler's Bliki
Creating Passionate Users

Archives

  • 2003
  • 2004
  • 2005
  • 2006
  • 2007
  • 2008
  • Subscribe
    RSS Exploring Solution Spaces XML


           
    2003.Nov.13 Thu

    TDD is Pokayoke

    Pokayoke is a way to prevent mistakes from being made, or to detect a mistake immediately. It is "fool-proofing".

    You might think that explicit/static typing is all we need for fool-proofing, but we know from experience that the presence of explicit typing does not eliminate bugs. Let's show this with an example in Java.

    Requirement: "The game displays the winning player's name in the game-log."

    class Player
    {
        public int score;
        public string name;
    };
    
    class GameLog
    {
        public string logText;
    
        public void appendText( string textIn )
        {
            // implementation left for the reader.
        }
    };
    
    class Game
    {
        public void displayWinningPlayer( Player playerOne, Player playerTwo, GameLog log )
        {
        }
    }
    

    Now the above code satisfies the compiler's explicit type-checking, but it doesn't satisfy the requirement. Let's build a way of detecting this failure to meet the requirements - using Test Driven Development.

    // ignore other JUnit scaffolding... just show the test...
    
    public void TestDisplayWinningPlayer() throws Exception
    {
        Player one;
        one.score = 12;
        one.name = "stan";
    
        Player two;
        two.score = 13;
        two.name = "kyle";
    
        GameLog log;
    
        Game game;
        game.displayWinningPlayer( one, two, log );
    
        assertEquals( "kyle won with 13 points", log.text );
    
        game.displayWinningPlayer( two, one, log );
    
        assertEquals( "kyle won with 13 points", log.text );
    }
    

    So how do we know the test is a valid one for the requirements? Well, it looks good to me. I ask my pair partner; he thinks it looks good, too. Since we're doing Extreme Programming, we ask our on-site customer what the log should look like. She looks at our test and says "This displayed text should should have an exclamation mark at the end." So we add "!" to the end of the strings in our tests.

    We also validate the test by running it now, even though the displayWinningPlayer function is empty. The test fails (as we expected) because the necessary code to make it pass hasn't been written yet. If it had passed, then we would know something is wrong with our test.

    assertion failure: expected 'kyle won with 13 points!' is not equal to actual ''.
    (line number and stack crawl follows.)
    

    Now we write just enough code to pass the tests. Unlike some examples in the TDD literature, I'm going to write the 'right' code directly, instead of taking tiny steps.

    class Game
    {
        public void displayWinningPlayer( Player playerOne, Player playerTwo, GameLog log )
        {
            if ( playerOne.score > playerTwo.score )
            {
                log.appendText( playerOne.name + " won with " + playerOne.score + " points!" );
            }
            else if ( playerTwo.score > playerOne.score )
            {
                log.appendText( playerTwo.name + " won with " + playerOne.score + " points!" );
            }
        }
    }
    

    We run the test and get this output:

    assertion failure: expected 'kyle won with 13 points!' is not equal to actual 'kyle won with 12 points!'.
    (line number and stack crawl follows.)
    

    Whoops. Our second assertion failed. We can immediately see that the failure has something to do the points, since it got the name right. We don't even have to get into the debugger. Doh! Look at the source: when player-two has the higher score, we still append player one's score. We fix it, and the now the test passes. Pokayoke.

    We ask the customer about ties, and he says that it can't happen according to the rules of the game. I would probably document that kind of 'negative requirement' with an assertion in the code. (The assertion has no effect in 'release builds' but it does have an effect in 'debug builds' that we use in test-driven development and manual pre-release testing).

        public void displayWinningPlayer( Player playerOne, Player playerTwo, GameLog log )
        {
            assert playerOne.score != playerTwo.score : "ties are not supposed to happen - fix your test/code";
            if ( playerOne.score > playerTwo.score )
            {
                log.appendText( playerOne.name + " won with " + playerOne.score + " points!" );
            }
            else if ( playerTwo.score > playerOne.score )
            {
                log.appendText( playerTwo.name + " won with " + playerTwo.score + " points!" );
            }
        }
    

    And one last point. I could have written all of the above in Python, which does not have explicit/static typing. The tests would still detect my failure to meet the requirement, and the code would be a lot shorter and more succinct.

    Extreme Programming has another way of detecting errors relatively quickly: "Frequence Releases." If possible, some end-users of this game will see versions of the game perhaps as often as every iteration. But that goes beyond pokayoke into the general area of feedback.

    [/docs] permanent link