Here's some Java code I was writing at the time I first noticed the pattern (November 2007):
That code tests that my implementation of ArrayUtil.shuffle() on an array of doubles works just like the implementation of Collections.shuffle(). One will notice that I called the first block or phase "initialization" and the last one "validation" (which, come to think of it, should have been called "verification" -- more on this at Wikipedia). The block labeled "double-check" should probably have been taken out into its own test./** * Tests the <i>shuffle</i> method against the Collections.shuffle(List<?>, * Random) implementation from which it was derived. */ @Test public void shuffle_AgainstReference ( ) { // { initialization Random randomSource; int length = 20; double[] sourceArray = new double[length]; List<Double> expectedList = new ArrayList<Double> ( length ); for ( int i = 0; i < length; i++ ) { sourceArray[i] = i; expectedList.add ( (double) i ); } // } // { double-check for ( int i = 0; i < length; i++ ) { String message = "Source array is different at index [" + i + "]"; assertEquals ( message, expectedList.get ( i ), sourceArray[i] ); } // } randomSource = new Random ( 42 ); ArrayUtil.shuffle ( sourceArray, randomSource ); randomSource = new Random ( 42 ); Collections.shuffle ( expectedList, randomSource ); // { validation for ( int i = 0; i < length; i++ ) { String message = "Shuffled array is different at index [" + i + "]"; assertEquals ( message, expectedList.get ( i ), sourceArray[i] ); } // } }
I recently noticed the more formalized use in the NamedStringFormatSolution.zip project (more about this project at Phil Haack's Named Formats Redux blog post), where the 3 phases of unit testing were explicitly called out by comments in the code:
The arrange phase is sometimes so trivial that its contents is folded into the act phase, but unless a value is repeated in several tests that it becomes cleaner or less error-prone to extract it out in a constant, it should remain in the test for maximum clarity.[Fact] public void Eval_WithNamedExpressionAndFormat_EvalsPropertyOfExpression() { //arrange var expr = new FormatExpression("{foo:#.##}"); //act string result = expr.Eval(new { foo = 1.23456 }); //assert Assert.Equal("1.23", result); }
So there you have it: the next time you write a test, design it to execute in three distinct phases of arrangement, acting and assertion. Your test will be better designed, easier to read and other maintainers will thank you for it.
2 comments:
I love this pattern too, although in an injection-heavy solution the "arrange" section will often represent the bulk of the work.
This pattern's fine, but in most cases its commonly used "arrange", "act" and "assert" comments are only pollution.
In the given example absolutely nothing is added by these comments:
//arrange
var expr = new FormatExpression("{foo:#.##}");
//act
string result = expr.Eval(new { foo = 1.23456 });
//assert
Assert.Equal("1.23", result);
Arranging in the same blocks without the comments is as effective and removes unnecessary distractions:
var expr = new FormatExpression("{foo:#.##}");
string result = expr.Eval(new { foo = 1.23456 });
Assert.Equal("1.23", result);
Post a Comment