<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:admin="http://webns.net/mvcb/" version="2.0">
  <channel>
    <title>No Fluff Just Stuff</title>
    <link>http://www.agileitx.com</link>
    <description>The best value in the Java/Open Source conferencing space hands down</description>
    <item>
      <title>WPF Composite Application Guidance is Live</title>
      <link>http://www.agileitx.com/blog_detail.jsp?rssItemId=124229&amp;utm_source=blogitem&amp;utm_medium=rss&amp;utm_campaign=blogrss</link>
      <description>&lt;p&gt;A very common pattern for business focused applications is the &amp;quot;Composite Application Pattern&amp;quot;.&amp;#160;&amp;#160; Over the last few months our P&amp;amp;P team has been working with industry leaders and the product teams in Microsoft to develop an sample application and a framework for building composite applications in WPF.&amp;#160; &lt;/p&gt;  &lt;p&gt;&lt;img height="221" alt="RIScreenshots_small.png" src="http://www.codeplex.com/Project/Download/FileDownload.aspx?ProjectName=CompositeWPF&amp;amp;DownloadId=37985" width="612" /&gt;&lt;/p&gt;  &lt;p&gt;If you are looking at building a WPF business focused application, you should really check this out.. all the documentation, source code and even unit tests are included!&lt;/p&gt;  &lt;p&gt;Check it out!&amp;#160; &lt;a href="http://msdn.microsoft.com/en-us/library/cc707819.aspx"&gt;Composite Application Guidance for WPF&lt;/a&gt;&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=8684125" width="1" height="1"&gt;</description>
      <pubDate>Sat, 05 Jul 2008 16:00:02 CDT</pubDate>
      <guid isPermaLink="true">91d46819-8472-40ad-a661-2c78acb4018c:8684125</guid>
      <dc:creator>Brad Abrams</dc:creator>
    </item>
    <item>
      <title>Coolidge Park by night [Flickr]</title>
      <link>http://www.agileitx.com/blog_detail.jsp?rssItemId=124228&amp;utm_source=blogitem&amp;utm_medium=rss&amp;utm_campaign=blogrss</link>
      <description>&lt;p&gt;&lt;a href="http://www.flickr.com/people/aarongustafson/"&gt;Aaron Gustafson&lt;/a&gt; posted a photo:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.flickr.com/photos/aarongustafson/2633299413/" title="Coolidge Park by night"&gt;&lt;img src="http://farm4.static.flickr.com/3082/2633299413_aedff3b814_m.jpg" width="240" height="180" alt="Coolidge Park by night" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/EasyReader/~4/325853340" height="1" width="1"/&gt;</description>
      <pubDate>Sat, 05 Jul 2008 13:00:02 CDT</pubDate>
      <guid isPermaLink="true">tag:flickr.com,2005:/photo/2633299413</guid>
      <dc:creator>Aaron Gustafson</dc:creator>
    </item>
    <item>
      <title>Testing Cocoa Controllers with OCMock</title>
      <link>http://www.agileitx.com/blog_detail.jsp?rssItemId=124232&amp;utm_source=blogitem&amp;utm_medium=rss&amp;utm_campaign=blogrss</link>
      <description>&lt;p&gt;For a few releases the Apple development tools have included OCUnit and many developers have now started to write unit tests. There are lots of tutorials that explain how this is done for the straight-forward cases but there&amp;#8217;s one area of testing that has proven difficult on most platforms, and that is testing of the user interface. That said, there are a few things that make this an easier problem to solve with Cocoa and in this post I&amp;#8217;ll explain why.&lt;/p&gt;
&lt;p&gt;&lt;span id="more-44"&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Let&amp;#8217;s look at an example. &lt;a href="http://ccmenu.sourceforge.net/"&gt;CCMenu&lt;/a&gt; is a small application that displays the status of &lt;a href="http://cruisecontrol.sourceforge.net/"&gt;CruiseControl&lt;/a&gt; continuous integration servers. As part of adding a new project to be monitored the user has to enter the URL for the CruiseControl server and a combox provides a history of previously used servers.&lt;/p&gt;
&lt;p&gt;&lt;a href='http://erik.doernenburg.com/wp-content/uploads/2008/07/ccmenu_snapshot1.png'&gt;&lt;img src="http://erik.doernenburg.com/wp-content/uploads/2008/07/ccmenu_snapshot1.png" alt="" title="CCMenu Snapshot 1" width="449" height="264" class="alignnone size-full wp-image-45" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The same dialog has a matrix of buttons to set the type of the server. Actually, by default CCMenu detects the server type automatically but that&amp;#8217;s beside the point. What we&amp;#8217;re interested in is the piece of functionality that is responsible for selecting the correct server type for the URL chosen by the user. In our case, this would be Cruise Control Dashboard.&lt;/p&gt;
&lt;p&gt;&lt;a href='http://erik.doernenburg.com/wp-content/uploads/2008/07/ccmenu_snapshot2.png'&gt;&lt;img src="http://erik.doernenburg.com/wp-content/uploads/2008/07/ccmenu_snapshot2.png" alt="" title="CCMenu Snapshot 2" width="449" height="264" class="alignnone size-full wp-image-46" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;One difference between Cocoa and most other UI frameworks is the fact that the user interface is stored by serialising the objects, rather than generating code. For this reason I&amp;#8217;m happy to not insist on test-first for the actual interface. What I do want to test is the code in the controller classes.&lt;/p&gt;
&lt;p&gt;Obviously, I could load the serialised objects, locate the elements involved and then use methods such as performClick: to trigger the actions. Sounds convoluted? Yes, I agree. Luckily there&amp;#8217;s a better way.&lt;/p&gt;
&lt;p&gt;This is a case where testing is tricky because the object under test interacts with objects that are difficult to deal with. In such cases dynamic mock objects have proven extremely useful. A good introduction can be found &lt;a href="http://martinfowler.com/articles/mocksArentStubs.html"&gt;here&lt;/a&gt; and &lt;a href="http://mockobjects.com/"&gt;mockobject.com&lt;/a&gt; lists papers and implementations. For Objective-C I created &lt;a href="http://www.mulle-kybernetik.com/software/OCMock/"&gt;OCMock&lt;/a&gt;. Let&amp;#8217;s look at how this helps us testing our controllers.&lt;/p&gt;
&lt;p&gt;Here are the relevant parts of the interface declaration of the controller. Given that I&amp;#8217;m doing test-driven development there&amp;#8217;s currently no implementation of the methods. I simply created the interface of the controller based on my needs while designing the dialog.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@interface CCMPreferencesController : CCMWindowController
{
    IBOutlet NSComboBox *serverUrlComboBox;
    IBOutlet NSMatrix *serverTypeMatrix;
}

- (IBAction)historyURLSelected:(id)sender;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In my test I want to use mock objects instead of loading the actual user interface. So, in the test setup, after creating my controller, I create appropriate mock objects and set up the controller to use these.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@implementation CCMPreferencesControllerTest

- (void)setUp
{
    controller = [[[CCMPreferencesController alloc] init] autorelease];
    serverUrlComboBoxMock = [OCMockObject mockForClass:[NSComboBox class]];
    [controller setValue:serverUrlComboBoxMock forKey:@"serverUrlComboBox"];
    serverTypeMatrixMock = [OCMockObject mockForClass:[NSMatrix class]];
    [controller setValue:serverTypeMatrixMock forKey:@"serverTypeMatrix"];
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You notice that I&amp;#8217;m using key-value coding to set the variables (outlets). I&amp;#8217;m doing this because the variables are not public and at the same time I don&amp;#8217;t want to write accessor methods for them.&lt;/p&gt;
&lt;p&gt;Now, we can start writing the actual test for the functionality. Have a look at the full test first.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;- (void)testSelectsServerTypeWhenHistoryURLIsSelected
{
    NSString *selectedUrl = @"http://localhost/cctray.xml";
    [[[serverUrlComboBoxMock stub] andReturn:selectedUrl] stringValue];
    [[serverTypeMatrixMock expect] selectCellWithTag:CCMCruiseControlDashboard];
    [controller historyURLSelected:nil];
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What&amp;#8217;s going on here? Firstly, I tell the mock object that stands in for the actual combox box that it should stub the stringValue method. This means that when somebody invokes stringValue on this object it will return the string @&amp;#8221;http://localhost/cctray.xml&amp;#8221;. This is all we&amp;#8217;d have done with the real combo box anyway.&lt;/p&gt;
&lt;p&gt;Secondly, I tell the mock that stands in for the server type matrix that it should expect that the method selectCellWithTag: is called with CCMCruiseControlDashboard as an argument.&lt;/p&gt;
&lt;p&gt;Lastly, I invoke the method I want to test. What happens, once the implementation is complete, is that the code will go the the combo box and ask it for its string value. The first mock will return the stubbed value. Now, the code should do whatever it needs to do to figure out which server type this corresponds to and then set that in the server type matrix by selecting the cell with the appropriate tag, and this is what we&amp;#8217;ve told the second mock to expect.&lt;/p&gt;
&lt;p&gt;Wait, you might say, we don&amp;#8217;t have an implementation yet. So, how does this test fail? It doesn&amp;#8217;t yet. We&amp;#8217;ll have to tell the mock objects to verify that everything we told them to expect actually occurred, and a logical place for this is the test tearDown.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;- (void)tearDown
{
    [serverUrlComboBoxMock verify];
    [serverTypeMatrixMock verify];
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Strictly speaking, we don&amp;#8217;t have to verify the combo box mock because it doesn&amp;#8217;t have any expectations but it&amp;#8217;s good practice to verify all mocks in the tear down, especially if the same mocks are used for multiple tests. By the way, by default the mocks also have fail-fast behaviour; when they receive a method that wasn&amp;#8217;t stubbed or expected, they raise an exception right away. Detecting something that wasn&amp;#8217;t expected can be done right way, detecting that something that was expected didn&amp;#8217;t occur must be trigger by the user.&lt;/p&gt;
&lt;p&gt;Now, if we run this test with an empty implementation of historyURLSelected: it will fail when we tell the server type matrix mock to verify because the expected method hasn&amp;#8217;t been called. The error message will look something like this:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Unknown.m:0: error: -[CCMPreferencesControllerTest testSelectsServerTypeWhenHistoryURLIsSelected] : OCMockObject[NSMatrix]: expected method was not invoked: selectCellWithTag:0&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Adding an implementation like the following one adds the right functionality and makes our test pass.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@implementation CCMPreferencesController

- (void)historyURLSelected:(id)sender
{
    NSString *serverUrl = [serverUrlComboBox stringValue];
    [serverTypeMatrix selectCellWithTag:[serverUrl cruiseControlServerType]];
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In summary, testing controllers becomes relatively easy when we follow this pattern:&lt;/p&gt;
&lt;p&gt;1. Replace all UI elements with mocks; using key-value coding to access the outlets.&lt;/p&gt;
&lt;p&gt;2. Set up stubs with return values for UI elements that the controller will query.&lt;/p&gt;
&lt;p&gt;3. Set up expectations for UI elements that the controller should manipulate.&lt;/p&gt;
&lt;p&gt;4. Invoke the method in the controller.&lt;/p&gt;
&lt;p&gt;5. Verify the expectations.&lt;/p&gt;
&lt;p&gt;I find tests following this pattern easier to write and understand than tests that load a NIB file and interact with the actual user interface elements.&lt;/p&gt;</description>
      <pubDate>Sat, 05 Jul 2008 11:00:02 CDT</pubDate>
      <guid isPermaLink="true">http://erik.doernenburg.com/?p=44</guid>
      <dc:creator>Erik Doernenburg</dc:creator>
    </item>
    <item>
      <title>Catching up on performance with the SitePen crew</title>
      <link>http://www.agileitx.com/blog_detail.jsp?rssItemId=124233&amp;utm_source=blogitem&amp;utm_medium=rss&amp;utm_campaign=blogrss</link>
      <description>&lt;p&gt;There&amp;#8217;s been so much going on in the performance space lately that I&amp;#8217;ve been snowed under.  It&amp;#8217;s difficult to know where to begin chronicling all of the progress.  I&amp;#8217;ll start with a few updates from Sitepen.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Back in April, Kris Zyp had a great article for IBM developerWorks called &lt;a href="http://www.ibm.com/developerworks/web/library/wa-aj-perform/?ca=dgr-lnxw01FasterAjax"&gt;Ajax performance analysis&lt;/a&gt;.  The developerWorks crew puts out some great material, and this is no exception.  Simply put, it&amp;#8217;s one of the best articles I&amp;#8217;ve seen on the topic, and it should be required reading for every Ajax developer.  He discusses Firebug, YSlow, and some client-side instrumentation techniques.&lt;/li&gt;
&lt;li&gt;Old friend of the Perf, Tom Trenka, had a nice post about &lt;a href="http://www.sitepen.com/blog/2008/05/09/string-performance-an-analysis/"&gt;string operations across browsers&lt;/a&gt; in May.  One of the more interesting takeaways is with regards to IE7 versus IE6.  The net &amp;#8212; there&amp;#8217;s no longer any justification, if there ever was, for special casing string concat operations for IE.&lt;/li&gt;
&lt;li&gt;One of my favorite tools, Firebug Lite, has seen some &lt;a href="&lt;a href="http://www.sitepen.com/blog/2008/06/02/firebug-lite-and-dojo-not-just-for-ie/"&gt;&amp;#8220;&gt;dramatic improvements&lt;/a&gt; in the Dojo Toolkit version, as discussed by Mike Wilcox in early June.  The features discussed: a popup mode that remembers size and position, ReCSS (so you can reload stylesheets without reloading the app), a DOM Inspector, an Object inspector, and a command line.  They&amp;#8217;ve definitely taken Firebug Lite a long way past the initial goal of offering a bare subset of Firebug functionality to IE developers.&lt;/li&gt;
&lt;li&gt;A few days ago, Mike posted another article &amp;#8212; this time with a nice addition to the recent swell of client-side profiling articles.  Mike whipped up a nice generic mechanism for &lt;a href="http://www.sitepen.com/blog/2008/07/01/a-quick-javascript-load-profiler/"&gt;tracker client side performance in a cookie&lt;/a&gt; to remove some of the tedium from generating a statistically relevant data set in your own browser.&lt;/li&gt;
&lt;li&gt;Finally, Alex Russell expands on the concept of lazy loading by creating a &lt;a href="http://www.sitepen.com/blog/2008/07/01/dojo-in-6k/"&gt;stub loader for Dojo&lt;/a&gt;.  Weighing in at a slim 6kB (gzipped over the wire), this build of dojo.js is just the bootstrap code necessary for loading the main functionality, all of which is deferred until it&amp;#8217;s actually called within an application.  John Resig &lt;a href="http://ejohn.org/blog/jquery-plugins-size-and-storage/"&gt;posted a follow-up regarding some of the clear downsides of this approach&lt;/a&gt;, such as the potential violation of user expectations.&lt;/li&gt;
&lt;/ul&gt;</description>
      <pubDate>Sat, 05 Jul 2008 08:00:06 CDT</pubDate>
      <guid isPermaLink="true">http://www.ajaxperformance.com/?p=123</guid>
      <dc:creator>Ryan Breen</dc:creator>
    </item>
    <item>
      <title>ASP.NET Ajax Roadmap Published</title>
      <link>http://www.agileitx.com/blog_detail.jsp?rssItemId=124263&amp;utm_source=blogitem&amp;utm_medium=rss&amp;utm_campaign=blogrss</link>
      <description>&lt;p&gt;&lt;a href="http://weblogs.asp.net/bleroy/default.aspx"&gt;&lt;a href="http://blogs.msdn.com/blogfiles/brada/WindowsLiveWriter/ASP.NETAjaxRoadmapPublished_63EE/image_2.png"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="133" alt="image" src="http://blogs.msdn.com/blogfiles/brada/WindowsLiveWriter/ASP.NETAjaxRoadmapPublished_63EE/image_thumb.png" width="197" align="right" border="0" /&gt;&lt;/a&gt;Bertrand&lt;/a&gt; recently published our &lt;a href="http://www.codeplex.com/aspnet/Release/ProjectReleases.aspx?ReleaseId=14924"&gt;roadmap for ASP.NET Ajax&lt;/a&gt;.&amp;#160; &lt;/p&gt;  &lt;p&gt;Our goal is to describe some of the proposed features that we are considering investing in future releases of ASP.NET AJAX, Visual Web Developer, and the ASP.NET AJAX Control Toolkit.&amp;#160;&amp;#160; &lt;br /&gt;We really appreciate your feedback, so this document is intended as much for you to provide an input to our direction as well as to give some indication of what the teams are investing in.&lt;/p&gt;  &lt;p&gt;We'd love to hear your thoughts!&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=8689358" width="1" height="1"&gt;</description>
      <pubDate>Fri, 04 Jul 2008 16:00:02 CDT</pubDate>
      <guid isPermaLink="true">91d46819-8472-40ad-a661-2c78acb4018c:8689358</guid>
      <dc:creator>Brad Abrams</dc:creator>
    </item>
    <item>
      <title>My Clutter is Different</title>
      <link>http://www.agileitx.com/blog_detail.jsp?rssItemId=124262&amp;utm_source=blogitem&amp;utm_medium=rss&amp;utm_campaign=blogrss</link>
      <description>&lt;p&gt;On the long weekends, Mark and I make a concerted effort to clean up the house. That means I have to address all my little piles: go through them, recycle what I can, throw out what can&amp;#8217;t be recycled, file others, figure out what to do with the rest. While Mark was helping me bring some of my paper and books downstairs, he nudged me about finishing the living room. &amp;#8220;I know you don&amp;#8217;t like clutter,&amp;#8221; he said. &amp;#8220;Yes, but I know where everything is. Besides, you have clutter, too.&amp;#8221; &amp;#8220;But I don&amp;#8217;t like your clutter,&amp;#8221; he responded. I started to say, &amp;#8220;Yeah, but my clutter is different&amp;#8221; at which point we both cracked up.&lt;/p&gt;
&lt;p&gt;My clutter is comfortable for me, otherwise I would have dealt with it already.�� You could call my clutter technical debt, and you&amp;#8217;d be right. I don&amp;#8217;t mind paying it off on long weekends. Otherwise, I would do something about it more often. But the reason my clutter is different is because it fits with my mental model of the world.�� I&amp;#8217;m sure when Mark reads this, he&amp;#8217;ll try to change my mental models. He&amp;#8217;s unlikely to be successful.&lt;/p&gt;
&lt;p&gt;These same kinds of discussions occur at work, but we tend to laugh at them less. (Maybe we should.) The next time you find yourself perturbed by someone else&amp;#8217;s perspective, consider this question: What would have to be true for the other person to be happy (or content or satisfied) with the situation?�� Partly, my clutter helps me see all the things I do, which is helpful. More clutter does not make it more helpful &lt;img src='http://jrothman.com/blog/mpd/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /&gt; &amp;#8212; there&amp;#8217;s a point at which even I think there&amp;#8217;s too much clutter. But seeing clutter doesn&amp;#8217;t help Mark, and since we share a house, I need to flex a bit. I&amp;#8217;ll continue cleaning up now.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/ManagingProductDevelopment?a=c6VQGJ"&gt;&lt;img src="http://feeds.feedburner.com/~f/ManagingProductDevelopment?i=c6VQGJ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/ManagingProductDevelopment?a=ZlWETJ"&gt;&lt;img src="http://feeds.feedburner.com/~f/ManagingProductDevelopment?i=ZlWETJ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/ManagingProductDevelopment?a=VZaBhj"&gt;&lt;img src="http://feeds.feedburner.com/~f/ManagingProductDevelopment?i=VZaBhj" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/ManagingProductDevelopment?a=MFRGij"&gt;&lt;img src="http://feeds.feedburner.com/~f/ManagingProductDevelopment?i=MFRGij" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/ManagingProductDevelopment?a=oCa3iJ"&gt;&lt;img src="http://feeds.feedburner.com/~f/ManagingProductDevelopment?i=oCa3iJ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/ManagingProductDevelopment?a=cgHMMJ"&gt;&lt;img src="http://feeds.feedburner.com/~f/ManagingProductDevelopment?i=cgHMMJ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/ManagingProductDevelopment/~4/326719261" height="1" width="1"/&gt;</description>
      <pubDate>Fri, 04 Jul 2008 13:00:02 CDT</pubDate>
      <guid isPermaLink="true">http://jrothman.com/blog/mpd/2008/07/my-clutter-is-different.html</guid>
      <dc:creator>Johanna Rothman</dc:creator>
    </item>
    <item>
      <title>On the radio</title>
      <link>http://www.agileitx.com/blog_detail.jsp?rssItemId=124234&amp;utm_source=blogitem&amp;utm_medium=rss&amp;utm_campaign=blogrss</link>
      <description>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp2.blogger.com/_HOfY71whEUc/SG33efPqV4I/AAAAAAAAAGk/uGp4FUtOvAo/s1600-h/radioshow5.jpg"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer;" src="http://bp2.blogger.com/_HOfY71whEUc/SG33efPqV4I/AAAAAAAAAGk/uGp4FUtOvAo/s320/radioshow5.jpg" alt="" id="BLOGGER_PHOTO_ID_5219099646347597698" border="0" /&gt;&lt;/a&gt;Just in case you were fortunate enough to miss it, I was on the &lt;a href="http://www.redmonk.com/cote/topic/podcasts/ria-weekly/"&gt;RIA Weekly&lt;/a&gt; radio show yesterday.  Actually, The RIA Weekly Show hosted by  Michael Cote of RedMonk (a favorite analyst company) and Ryan Stuart of Adobe (a stand up guy) is a great show. Every week they talk about current events in the RIA industry and mix in a good amount of chuckles.&lt;br /&gt;&lt;br /&gt;I was the guest on &lt;a href="http://www.redmonk.com/cote/2008/07/03/episode-17-curls-richard-monson-haefel-ria-middleware-search-for-flash/"&gt;Episode 17&lt;/a&gt;. I spoke mostly about Curl and how its different from other RIA solutions. We also spoke a little bit about JSF, SOA, programing languages, and other topics. Over all I think it went pretty well, but its never fun to see yourself on video or listen to yourself on a voice recording. Apparently I do an in-take-of-air after every sentence as if I'm living on a respirator and need to get the mask back on or suffocate.</description>
      <pubDate>Fri, 04 Jul 2008 11:00:02 CDT</pubDate>
      <guid isPermaLink="true">tag:blogger.com,1999:blog-7867849070722123199.post-6254929698011580044</guid>
      <dc:creator>Richard Monson-Haefel</dc:creator>
    </item>
    <item>
      <title>Where the hell is Matt?</title>
      <link>http://www.agileitx.com/blog_detail.jsp?rssItemId=124235&amp;utm_source=blogitem&amp;utm_medium=rss&amp;utm_campaign=blogrss</link>
      <description>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp1.blogger.com/_HOfY71whEUc/SG34RpyYlLI/AAAAAAAAAGs/wtWx8W_W6sY/s1600-h/matt.jpg"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer;" src="http://bp1.blogger.com/_HOfY71whEUc/SG34RpyYlLI/AAAAAAAAAGs/wtWx8W_W6sY/s320/matt.jpg" alt="" id="BLOGGER_PHOTO_ID_5219100525350917298" border="0" /&gt;&lt;/a&gt;"Matt is a 31-year-old deadbeat from Connecticut who used to think that all he ever wanted to do in life was make and play videogames. Matt achieved this goal pretty early and enjoyed it for a while, but eventually realized there might be other stuff he was missing out on. In February of 2003, he quit his job in Brisbane, Australia and used the money he'd saved to wander around Asia until it ran out. He made this site so he could keep his family and friends updated about where he is.A few months into his trip, a travel buddy gave Matt an idea. They were standing around taking pictures in Hanoi, and his friend said "Hey, why don't you stand over there and do that dance. I'll record it." He was referring to a particular dance Matt does. It's actually the only dance Matt does. He does it badly. Anyway, this turned out to be a very good idea."&lt;br /&gt;                                               - Patrick Drury&lt;br /&gt;&lt;br /&gt;See Matt dancing &lt;a href="http://www.wherethehellismatt.com/videos.shtml?fbid=B5TuEj"&gt;video&lt;/a&gt;.</description>
      <pubDate>Fri, 04 Jul 2008 08:00:02 CDT</pubDate>
      <guid isPermaLink="true">tag:blogger.com,1999:blog-7867849070722123199.post-5966389776808303075</guid>
      <dc:creator>Richard Monson-Haefel</dc:creator>
    </item>
    <item>
      <title>Json-lib: 2 years and counting</title>
      <link>http://www.agileitx.com/blog_detail.jsp?rssItemId=124231&amp;utm_source=blogitem&amp;utm_medium=rss&amp;utm_campaign=blogrss</link>
      <description>Yikes! been so busy at work that missed &lt;a href="http://json-lib.sourceforge.net/"&gt;Json-lib&lt;/a&gt;'s second anniversary by two days. Don't have exact download stats as sourceforge.net decided to give broken links for projects statistics for over a week now, last time I checked we were over the 78K mark &lt;img src="http://www.jroller.com/images/smileys/wink.gif" class="smiley" alt=";-)" title=";-)" /&gt;&lt;br/&gt;&lt;br/&gt;20 releases, 71 bug reports, 72 feature requests, 6 patches, 123 forum messages and countless direct emails we are still up and going strong. Thanks to everybody that has contributed to the project, and to the users too, we wouldn't be here if it wasn't because of you.</description>
      <pubDate>Thu, 03 Jul 2008 16:00:03 CDT</pubDate>
      <guid isPermaLink="true">http://www.jroller.com/aalmiray/entry/json_lib_2_years_and</guid>
      <dc:creator>Andres Almiray</dc:creator>
    </item>
    <item>
      <title>“Physical” Request-Scoped Attributes for JSF in Portlets</title>
      <link>http://www.agileitx.com/blog_detail.jsp?rssItemId=124230&amp;utm_source=blogitem&amp;utm_medium=rss&amp;utm_campaign=blogrss</link>
      <description>&lt;div class='snap_preview'&gt;&lt;br /&gt;&lt;p&gt;One of the more frustrating parts of portal development is the fact that the Portal Specification does not follow good &amp;#8220;design by contract&amp;#8221; principals.�� One of the more annoying aspects of this deficiency is how the behavior of request-scoped attributes is defined in the portlet specification.&lt;/p&gt;
&lt;p&gt;In the servlet world, there is one Request object for each physical request from the the browser.�� This is called a single-phased request.�� Because of their complexity and the need to enable applications to perform better, the Portlet 1.0 specification introduced a multi-phased request in which certain types of requests are run in response to certain types of requests.�� This means that rendering logic can be light and fast in its implmentation while data-fetching logic and processing could be done only when a portlet is the target of its own request.&lt;/p&gt;
&lt;p&gt;Although this approach has merit, the Portal Specification does not clearly outline the lifespan of a request-scoped attribute.�� Many portal vendors (quite incorrectly in my opinion) say that items added to the request during the processAction will NOT be available durring the render.�� While other vendors, including the R.I. do preserve these attributes between an action and it&amp;#8217;s following render request.�� This means that different containers behave differently depending on their implementation, which is something that directly violates &amp;#8220;Design by Contract&amp;#8221; principals.&lt;/p&gt;
&lt;h2&gt;JSR-301 Portlet Bridge&amp;#8217;s Take&lt;/h2&gt;
&lt;p&gt;The JSR-301 Specification tries to address this inconsistency for JSF applications by stating that Request Attributes which are added during a JSF Action phase of the lifecycle, should be available during the following render and all subsequent renders.�� This allows a situation where a JSF portlet which needs to be rendered as a result of another portlet&amp;#8217;s action, can simply continue by running the render portions of the lifecycle.�� This behavior happens regardless of behavior and although there may be some challanges in dealing with this nuance, it will remain consistent across portal boundries.&lt;/p&gt;
&lt;p&gt;For those attributes that should NOT be around for subsequent renders, like per-request state information, JSR-301 added a special configuration as well as an &amp;#8220;@ExcludeFromManagedRequestScope&amp;#8221; attribute which allows an attribute to remove itself from this feature.�� For example, let&amp;#8217;s say a renderkit stores an attribute on the request which holds a flag telling the renderkit to render in XML (for an Ajax request) rather then html.�� If this bean persisted to multiple requests, you might find that your renderkit would render ajax content directly into your portlet&amp;#8217;s window on the portal page.�� This is NOT the desired result.&lt;/p&gt;
&lt;p&gt;Where JSR-301 falls short, however, is that it does not enforce any sort of consistency when a bean is excluded from the managed request scope, instead it defers to the logic defined by the portal container.�� As such, attributes which are excluded from the managed request scope suffer from the same implementation-specific problems that the Portlet 1.0 containers suffer from.&lt;/p&gt;
&lt;h2&gt;The good stuff&lt;/h2&gt;
&lt;p&gt;There are several ways to handle this situation.�� The first is to make sure that beans are able to be re-created on an as-needed basis, even from a portlet render request.�� This is a challange in a portlet environment because parameters which are passed into the action are not available durring the subsequent render request unless they are explicitly added.�� Furthermore, if the parameters are added, they will be added to every Render request until either a render request is called specifically or until antoher action is called on the portlet.�� In either case, this suffers the same pitfalls as the bridge&amp;#8217;s managed request scope, so if your initializaion state is dependent on incomming parameters for your initialization, it will be very difficult for you to manage this manually.&lt;/p&gt;
&lt;p&gt;Another approach, and the one which I will concentrate on here, is to develop your own mechanism for preserving request attributes from action to render.�� Before I get started, I also want to make some comments about the code.�� This code it an example which I&amp;#8217;m using to illustrate an idea.�� As such, I&amp;#8217;m not worrying about thread safety and robustness so much as I&amp;#8217;m trying to illustrate an idea.�� If you want a pre-canned solution, I will likely be adding one to the configurator mechanism that I currently have under development, so keep watching this blog for further details.&lt;/p&gt;
&lt;p&gt;The code below outlines the usage of a custom FacesContext Decorator.&lt;/p&gt;
&lt;pre name="code" class="java"&gt;

  public class FacesContextImpl implements FacesContext
  {
    private FacesContext _orig;
    public static final String STATE_NAME = &amp;quot;scottobryan.STATE&amp;quot;;
    public FacesContextImpl(FacesContext context)
    {
      _orig = context;
      ExternalContext ec = getExternalContext();
      Map&amp;lt;String, Object&amp;gt; saved = null;
      if(ec.getRequest() instanceof RenderRequest)
      {
        /*
         *First we look to see if we have a stored state
         *For this example I&amp;#039;m using the session but you can
         *use other scopes
         */
        saved = (Map&amp;lt;String, Object&amp;gt;)ec.getSessionMap()
                                         .remove(STATE_NAME);
      }

      if(saved == null)
      {
        saved = new HashMap&amp;lt;String, Object&amp;gt;();
      }

      ec.getRequestMap().put(STATE_NAME, saved);

      //TODO your other logic here
    }

    //TODO all other methods should delegate except release
    @Override
    public void release()
    {
      ExternalContext ec = getExternalContext();
      if (ActionRequest) instanceof ec.getRequest())
      {
        Map&amp;lt;String, Object&amp;gt; state =
         (Map&amp;lt;String, Object&amp;gt;)ec.getRequestMap()
                                  .remove(STATE_NAME);
        ec.getSessionMap().put(STATE_NAME, state);
      }

      _orig.release();
    }
  }
&lt;/pre&gt;
&lt;p&gt;Lines 17 and 18 are responsible for trying to get the state off the session if it exists.�� If it does not then a new state map is created regardless of environment.&lt;/p&gt;
&lt;p&gt;Lines 33 to 46 will save the map to the session when the FacesContext is release by the bridge.�� It will only do this on an action request since that&amp;#8217;s the only usecase we care about.�� For cleanliness I go ahead and remove it from the request, but an uglier (albeit faster) technique might be to try to leave it on the request so that durring the next render you can figure out whether you are in a container which preserves request attributes and are therefore able to disable this caching alltogether.&lt;/p&gt;
&lt;p&gt;The code will make sure that there will be a map stored on the request attributes by the name of STATE_NAME and that any attributes which are appended to it will be available throughout the lifetime of the physical request.�� If you have a container that is not tolerant of non-serializable data on the session (by spec it should be, just not for failover), you can either make all your stuff serializable or you can play around with appending it to the Application scope.&lt;/p&gt;
&lt;p&gt;Also, it might be good to be able to encode some sort of id that can be used to tell whether the state belongs to a certain render request or not in case something happens and the render does not follow the action for which is was intended.�� This could happen as a result of a network hickup on a remote (WSRP) portal, but I&amp;#8217;m hoping that in such curcumstances portals are able to recover better.&lt;/p&gt;
&lt;p&gt;Enjoy!&lt;/p&gt;
&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/categories/scottobryan.wordpress.com/28/" /&gt; &lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/tags/scottobryan.wordpress.com/28/" /&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/scottobryan.wordpress.com/28/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/scottobryan.wordpress.com/28/" /&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/scottobryan.wordpress.com/28/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/scottobryan.wordpress.com/28/" /&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/scottobryan.wordpress.com/28/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/scottobryan.wordpress.com/28/" /&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/scottobryan.wordpress.com/28/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/scottobryan.wordpress.com/28/" /&gt;&lt;/a&gt; &lt;a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/scottobryan.wordpress.com/28/"&gt;&lt;img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/scottobryan.wordpress.com/28/" /&gt;&lt;/a&gt; &lt;img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=scottobryan.wordpress.com&amp;blog=4005520&amp;post=28&amp;subd=scottobryan&amp;ref=&amp;feed=1" /&gt;&lt;/div&gt;</description>
      <pubDate>Thu, 03 Jul 2008 13:00:03 CDT</pubDate>
      <guid isPermaLink="true">http://scottobryan.wordpress.com/?p=28</guid>
      <dc:creator>Scott  O'Bryan</dc:creator>
    </item>
    <item>
      <title>Across the bridge [Flickr]</title>
      <link>http://www.agileitx.com/blog_detail.jsp?rssItemId=124227&amp;utm_source=blogitem&amp;utm_medium=rss&amp;utm_campaign=blogrss</link>
      <description>&lt;p&gt;&lt;a href="http://www.flickr.com/people/aarongustafson/"&gt;Aaron Gustafson&lt;/a&gt; posted a photo:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.flickr.com/photos/aarongustafson/2634122288/" title="Across the bridge"&gt;&lt;img src="http://farm4.static.flickr.com/3156/2634122288_fe5a3f1817_m.jpg" width="240" height="180" alt="Across the bridge" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/EasyReader/~4/325853337" height="1" width="1"/&gt;</description>
      <pubDate>Thu, 03 Jul 2008 11:00:02 CDT</pubDate>
      <guid isPermaLink="true">tag:flickr.com,2005:/photo/2634122288</guid>
      <dc:creator>Aaron Gustafson</dc:creator>
    </item>
    <item>
      <title>Links for 2008-07-02 [ma.gnolia]</title>
      <link>http://www.agileitx.com/blog_detail.jsp?rssItemId=124223&amp;utm_source=blogitem&amp;utm_medium=rss&amp;utm_campaign=blogrss</link>
      <description>&lt;ul&gt;
&lt;li&gt;&lt;a href="http://uk.youtube.com/watch?v=9qCySJG_xrg"&gt;Composition&lt;/a&gt;&lt;br/&gt;
&lt;p&gt;&lt;a href="http://uk.youtube.com/watch?v=9qCySJG_xrg"&gt;&lt;img alt="Composition" src="http://ma.gnolia.com/bookmarks/vufashota/thumbnail" /&gt;&lt;/a&gt;&lt;/p&gt;
                
&lt;p&gt;Somewhere in this film is one of my photos of the Sydney Opera House. Very cool short film.&lt;/p&gt;

&lt;p&gt;Saved By: &lt;a href="http://ma.gnolia.com/people/aarongustafson" title="Visit Aaron Gustafson on Ma.gnolia"&gt;Aaron Gustafson&lt;/a&gt; | &lt;a href="http://ma.gnolia.com/people/aarongustafson/bookmarks/vufashota" title="View Composition on Ma.gnolia"&gt;View Details&lt;/a&gt; | &lt;a href="http://ma.gnolia.com/bookmarks/vufashota/thanks/feed/confirm"&gt;Give Thanks&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tags:&lt;/strong&gt; &lt;a href="http://ma.gnolia.com/people/aarongustafson/tags/%22Sydney+Opera+House%22" rel="tag" title="Find aarongustafson bookmarks tagged '&amp;quot;Sydney Opera House&amp;quot;'"&gt;"Sydney Opera House"&lt;/a&gt;, &lt;a href="http://ma.gnolia.com/people/aarongustafson/tags/film" rel="tag" title="Find aarongustafson bookmarks tagged 'film'"&gt;film&lt;/a&gt;, &lt;a href="http://ma.gnolia.com/people/aarongustafson/tags/creative+commons" rel="tag" title="Find aarongustafson bookmarks tagged 'creative commons'"&gt;creative commons&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;img src="http://feeds.feedburner.com/~r/EasyReader/~4/325484279" height="1" width="1"/&gt;</description>
      <pubDate>Thu, 03 Jul 2008 08:00:02 CDT</pubDate>
      <guid isPermaLink="true">http://ma.gnolia.com/people/aarongustafson/bookmarks#2008-07-02</guid>
      <dc:creator>Aaron Gustafson</dc:creator>
    </item>
    <item>
      <title>Hidden folders in OS X file dialogs</title>
      <link>http://www.agileitx.com/blog_detail.jsp?rssItemId=124211&amp;utm_source=blogitem&amp;utm_medium=rss&amp;utm_campaign=blogrss</link>
      <description>&lt;p&gt;Found a good shortcut for getting access to hidden folders in OS X file dialogs and the Finder. It requires some typing and it doesn&amp;#8217;t auto-complete like Linux does, but it is better than nothing. Just hit Shift-Command-G to open the &amp;#8220;Go To Folder&amp;#8221; dialog and then type the path to the hidden file or folder. The path can be relative.&lt;/p&gt;
&lt;p&gt;Now, just need to figure out how to access hidden files from the Finder and file dialogs.&lt;/p&gt;</description>
      <pubDate>Wed, 02 Jul 2008 16:00:03 CDT</pubDate>
      <guid isPermaLink="true">http://brian.pontarelli.com/2008/07/01/hidden-folders-in-os-x-file-dialogs/</guid>
      <dc:creator>Brian Pontarelli</dc:creator>
    </item>
    <item>
      <title>Compass 2.1.0 M1 Released</title>
      <link>http://www.agileitx.com/blog_detail.jsp?rssItemId=124214&amp;utm_source=blogitem&amp;utm_medium=rss&amp;utm_campaign=blogrss</link>
      <description>&lt;p&gt;&lt;a href="http://www.compass-project.org"&gt;Compass&lt;/a&gt; version 2.1.0 M1 released. The release includes several features including a much improved XSEM support (namespace xpath, better stax integration, jdom integration), &lt;a href="http://www.kimchy.org/dynamic-mappings-settings-with-compass/"&gt;Dynamic mappings removal and additions&lt;/a&gt;, &lt;a href="http://www.kimchy.org/json-mappings-with-compass/"&gt;JSEM - JSON to Search Engine Mapping&lt;/a&gt; support, and &lt;a href="http://www.kimchy.org/collocated-indexing-and-distributed-search-with-gigaspaces/"&gt;Collocated integration and distributed search with GigaSpaces&lt;/a&gt;. It also includes several bug fixes (with the important ones backported to 2.0 branch). Enjoy!.&lt;/p&gt;
&lt;img src="http://feeds.feedburner.com/~r/kimchyblog/~4/324347569" height="1" width="1"/&gt;</description>
      <pubDate>Wed, 02 Jul 2008 13:00:03 CDT</pubDate>
      <guid isPermaLink="true">http://www.kimchy.org/compass-210-m1-released/</guid>
      <dc:creator>Shay Banon</dc:creator>
    </item>
    <item>
      <title>Where the hell is Matt?</title>
      <link>http://www.agileitx.com/blog_detail.jsp?rssItemId=124216&amp;utm_source=blogitem&amp;utm_medium=rss&amp;utm_campaign=blogrss</link>
      <description>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.wherethehellismatt.com/videos.shtml?fbid=B5TuEj"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer;" src="http://bp0.blogger.com/_HOfY71whEUc/SGuUHmPlrCI/AAAAAAAAAF8/m7DZXMi5gh8/s320/wherethehellismatt.jpg" alt="" id="BLOGGER_PHOTO_ID_5218427451484777506" border="0" /&gt;&lt;/a&gt;&lt;a href="http://www.wherethehellismatt.com/videos.shtml?fbid=B5TuEj"&gt;I really needed this today&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;"Matt is a 31-year-old deadbeat from Connecticut who used to think that all he ever wanted to do in life was make and play videogames. Matt achieved this goal pretty early and enjoyed it for a while, but eventually realized there might be other stuff he was missing out on. In February of 2003, he quit his job in Brisbane, Australia and used the money he'd saved to wander around Asia until it ran out. He made this site so he could keep his family and friends updated about where he is.A few months into his trip, a travel buddy gave Matt an idea. They were standing around taking pictures in Hanoi, and his friend said "Hey, why don't you stand over there and do that dance. I'll record it." He was referring to a particular dance Matt does. It's actually the only dance Matt does. He does it badly. Anyway, this turned out to be a very good idea."&lt;br /&gt;                                                   - Patrick Drury</description>
      <pubDate>Wed, 02 Jul 2008 11:00:02 CDT</pubDate>
      <guid isPermaLink="true">tag:blogger.com,1999:blog-7867849070722123199.post-5966389776808303075</guid>
      <dc:creator>Richard Monson-Haefel</dc:creator>
    </item>
    <item>
      <title>It’s ok to wet yourself every once in awhile</title>
      <link>http://www.agileitx.com/blog_detail.jsp?rssItemId=124215&amp;utm_source=blogitem&amp;utm_medium=rss&amp;utm_campaign=blogrss</link>
      <description>&lt;p&gt;Dan North, the veritable progenitor of &lt;a href="http://dannorth.net/introducing-bdd"&gt;behavior driven development&lt;/a&gt; (or BDD), &lt;a href="http://dannorth.net/2008/06/let-your-examples-flow"&gt;recently blogged&lt;/a&gt; about unnecessary &lt;a href="http://en.wikipedia.org/wiki/Don't_repeat_yourself"&gt;DRY&lt;/a&gt;ness (meaning don&amp;#8217;t repeat yourself) with respect to &lt;em&gt;clarity of intent&lt;/em&gt; when it comes to testing (in generic terms of the word). Essentially, in the case of a &lt;a href="http://www.junit.org/"&gt;JUnit&lt;/a&gt; test, for example, by utilizing a &lt;code&gt;setUp&lt;/code&gt; method and  possibly other helper methods, the test itself becomes somewhat cluttered&amp;#8211; one must jump around the code to truly understand the intention of the test in the first place. &lt;img style="PADDING-LEFT: 0.5em; PADDING-RIGHT: 1.0em; PADDING-TOP: 1.0em; FLOAT: LEFT; PADDING-BOTTOM: 1.0em" src="http://thediscoblog.com/images/shhh.jpg" alt="shh!" width="384" height="263"/&gt;&lt;/p&gt;
&lt;p&gt;In fact, Dan says it quite nicely&amp;#8211; tests (or stories, baby) are:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;
&amp;#8220;examples [that] tell a story about what the code does&amp;#8230; [and] clarity of intent is found in the quality of the narrative, not necessarily in minimising duplication.&amp;#8221;
&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Thus, over &lt;em&gt;utilizing&lt;/em&gt; the DRY principle can be ineffective when it comes to testing; indeed, he makes a great point! If tests or stories are intended to serve as &amp;#8220;executable documention&amp;#8221; doesn&amp;#8217;t it make sense to make them as easy to read and understand as possible?  &lt;/p&gt;
&lt;p&gt;As such, because it&amp;#8217;s my bag, I took the liberty of pondering the depth of favored term DRY and decided that when it comes to clearly expressing intent with respect to stories (as in the case of &lt;a href="http://easyb.org/"&gt;easyb&lt;/a&gt;) or tests, it often pays to be &lt;em&gt;WET&lt;/em&gt;&amp;#8211; that is, &lt;u&gt;w&lt;/u&gt;holly &lt;u&gt;e&lt;/u&gt;xpress your &lt;u&gt;t&lt;/u&gt;actics, baby. &lt;/p&gt;
&lt;p&gt;You see, applying WET to, say, an &lt;a href="http://easyb.org/howtos.html"&gt;easyb story&lt;/a&gt; then yields perhaps more &lt;em&gt;text&lt;/em&gt;, but it leaves no room for misinterpretation, man. For example, imagine a story regarding discounts (this is my touchstone example that is in danger of itself becoming dry&amp;#8211; no pun intended either, man). The high level story is such that VIP customers receive varying discounts depending on the amount of money a particular order has&amp;#8211; you could even require a minimum number of items too (if you were attempting to move inventory). Thus, one scenario could be:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;scenario "VIP customer with 3 items, over $30 receiving 10% discount", {
  given "a VIP customer"
  and "given they have at least 3 items totaling over $30"
  when "they proceed to checkout"
  then "they should receive a 10% discount"
} &lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Of course, from here, stakeholders, through a collaborative effort, realize more scenarios are possible&amp;#8211; for instance, the VIP customer has 3 items totaling over $100&amp;#8211; thus, a higher discount is applied. A first stab of staying WET (that is, &lt;u&gt;w&lt;/u&gt;holly &lt;u&gt;e&lt;/u&gt;xpressing your &lt;u&gt;t&lt;/u&gt;actics, man) yields this scenario: &lt;/p&gt;
&lt;pre&gt;&lt;code&gt;scenario "VIP customer with 3 items, over $100 receiving 15% discount", {
  given "a VIP customer"
  and "given they have at least 3 items totaling over $100"
  when "they proceed to checkout"
  then "they should receive a 15% discount"
} &lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Reading these scenarios (which are executable, by the way!) leaves no room for missing the boat&amp;#8211; the tactics involved are wholly expressed!!&amp;#8211; reading them is somewhat effortless from the standpoint that you don&amp;#8217;t necessarily need to jump around the file to gain a clear understanding of context. &lt;/p&gt;
&lt;p&gt;If you prefer staying DRY, the story can become more concise&amp;#8211; &lt;/p&gt;
&lt;pre&gt;&lt;code&gt;before_each "a VIP customer with 3 items is assumed", {
  given "a VIP customer"
  and "given they have at least 3 items"
}

scenario "VIP customer with 3 items, over $30 receiving 10% discount", {
  given "the 3 items total at least $30"
  when "they proceed to checkout"
  then "they should receive a 10% discount"
} 

scenario "VIP customer with 3 items, over $100 receiving 15% discount", {
  given "the 3 items total at least $100"
  when "they proceed to checkout"
  then "they should receive a 15% discount"
} &lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Both stories convey intent&amp;#8211; it is just that the WET one is more clearly expressed&amp;#8211; in the DRY example, as more and more scenarios are added (and they surely will), one needs to jump to the top to gain an understanding of the underlying assumption (that is, a VIP customer with 3 items in their shopping cart). &lt;/p&gt;
&lt;p&gt;DRY is fundamentally a sound principle&amp;#8211; I&amp;#8217;m not here to deny that; however, like all good things, it can be overused without regard for a particular situation. In fact, &lt;a href="http://en.wikipedia.org/wiki/Goethe"&gt;Johann Wolfgang von Goethe&lt;/a&gt; (of Faust fame, baby) is quoted thus: &lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;
&amp;#8220;The phrases that men hear or repeat continually, end by becoming convictions and ossify the organs of intelligence.&amp;#8221;
&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Because it is everyone&amp;#8217;s bag, baby, think through DRYness from time to time and realize that in some cases, it is perfectly acceptable to WET yourself (I mean, to practice WET).  Can you dig it, man?&lt;/p&gt;
 &lt;p&gt;&lt;center&gt;I also blog at &lt;a href="http://www.testearly.com"&gt;testearly.com&lt;/a&gt; | &lt;a href="http://www.stelligent.com/"&gt;My company&lt;/a&gt; is &lt;a href="http://www.stelligent.com/content/view/8/39/"&gt;hiring gurus&lt;/a&gt;&lt;/center&gt;&lt;/p&gt;</description>
      <pubDate>Wed, 02 Jul 2008 08:00:02 CDT</pubDate>
      <guid isPermaLink="true">http://thediscoblog.com/2008/07/02/its-ok-to-wet-yourself-every-once-in-awhile/</guid>
      <dc:creator>Andrew Glover</dc:creator>
    </item>
    <item>
      <title>Loving the South African Developer Community</title>
      <link>http://www.agileitx.com/blog_detail.jsp?rssItemId=124213&amp;utm_source=blogitem&amp;utm_medium=rss&amp;utm_campaign=blogrss</link>
      <description>&lt;p&gt;I just got back from South Africa and I am happy to report that the .NET Developer community is very strong there.&amp;#160;&amp;#160;&amp;#160; &lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;em&gt;User's Group Meetings in in Johannesburg and Cape Town&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;User group meeting in Johannesburg&amp;#160; - thanks for setting this up &lt;a href="http://craign.net/2008/05/30/silverlight-2-with-brad-abrams/"&gt;Craig Nicholson&lt;/a&gt;..&amp;#160; I had a great time showing off Silverlight 2.&amp;#160; We had lot of fun talking about Silverlight wit these &lt;a href="http://brad_abrams.members.winisp.net/Projects/SouthAfrica08/Silverlight2-RIA.pptx"&gt;slides&lt;/a&gt; and &lt;a href="http://blogs.msdn.com/brada/archive/2008/06/26/data-focused-silverlight-demo.aspx"&gt;demo&lt;/a&gt; that I did.&amp;#160; I also showed &lt;a href="http://memorabilia.hardrock.com/"&gt;HardRock DeepZoom&lt;/a&gt;, the video wall and &lt;a href="http://www.hsn.tv/"&gt;HSN.TV&lt;/a&gt; &lt;/li&gt;    &lt;li&gt;User group meeting in Cape Town - Thanks to &lt;a href="http://hilton.giesenow.com/archive/2008/05/14/s-a-developer-net-event-june-2008-mvc-with-brad-abrams.aspx"&gt;Hilton Giesenow&lt;/a&gt; for setting this up.&amp;#160; This time I did a drill down into the ASP.NET MVC framework.&amp;#160; The response was excellent!&amp;#160;&amp;#160; Thanks folks for your questions and interest!&amp;#160; Here is the &lt;a href="http://brad_abrams.members.winisp.net/Projects/SouthAfrica08/MVC-Presentation.pptx"&gt;slides&lt;/a&gt; and &lt;a href="http://brad_abrams.members.winisp.net/Projects/SouthAfrica08/MVCNorthwindDemo.zip"&gt;demo&lt;/a&gt;..&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;b&gt;&lt;i&gt;Mix Essentials in Johannesburg and Cape Town&lt;/i&gt;&lt;/b&gt;&lt;/p&gt;  &lt;p&gt;We had over 664 developers\designers attend the events in both cities&amp;#8230; A little over 3/5&lt;sup&gt;th&lt;/sup&gt; of them were developers and the vast majority of them were active ASP.NET developers.&amp;#160; Just about everyone was already on VS2008.&amp;#160;&amp;#160;&amp;#160; &lt;/p&gt;  &lt;p&gt;&lt;a href="http://blogs.msdn.com/blogfiles/brada/WindowsLiveWriter/LovingtheSouthAfricanDeveloperCommunity_8F7A/clip_image001_2.jpg"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="125" alt="clip_image001" src="http://blogs.msdn.com/blogfiles/brada/WindowsLiveWriter/LovingtheSouthAfricanDeveloperCommunity_8F7A/clip_image001_thumb.jpg" width="144" border="0" /&gt;&lt;/a&gt; &lt;a href="http://blogs.msdn.com/blogfiles/brada/WindowsLiveWriter/LovingtheSouthAfricanDeveloperCommunity_8F7A/clip_image002_2.jpg"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="126" alt="clip_image002" src="http://blogs.msdn.com/blogfiles/brada/WindowsLiveWriter/LovingtheSouthAfricanDeveloperCommunity_8F7A/clip_image002_thumb.jpg" width="146" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;u&gt;Keynote:&lt;/u&gt;&lt;/p&gt;  &lt;p&gt;We kicked off the day with keynote highlighting Microsoft&amp;#8217;s investment in UX.&amp;#160; I got drafted to do a breif bit on to show off some of the great work we are doing in &lt;b&gt;WPF for SP1&lt;/b&gt;. So I showed the very cool WPF Bitmaps effects demo ScottGu showed at Mix '08 in Vegas&amp;#8230; Judging from the comments afterward, I think it got a few people thinking about what is possible with a desktop applications.&amp;#160; People were impressed that the CPU was not being chewed up by this.. &lt;/p&gt;  &lt;p&gt;&lt;a href="http://blogs.msdn.com/blogfiles/brada/WindowsLiveWriter/LovingtheSouthAfricanDeveloperCommunity_8F7A/clip_image003_2.jpg"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="141" alt="clip_image003" src="http://blogs.msdn.com/blogfiles/brada/WindowsLiveWriter/LovingtheSouthAfricanDeveloperCommunity_8F7A/clip_image003_thumb.jpg" width="244" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;For the rest of the day we split up &amp;#8211; the designers when to a different room and I kept all of the developers.&amp;#160; I did three sessions covering the full web development space.&amp;#160;&amp;#160;&amp;#160; The retention rate was really good&amp;#8230; just about everyone stuck around for the end.&amp;#160; &lt;u&gt;&lt;/u&gt;&lt;/p&gt;  &lt;p&gt;&lt;u&gt;&lt;/u&gt;&lt;/p&gt;  &lt;p&gt;&lt;u&gt;Silverlight 2:&lt;/u&gt;&lt;/p&gt;  &lt;p&gt;Next up I did an hour fifteen minute demo on Silverlight 2 development.&amp;#160;&amp;#160; I focused on end-to-end development, not just the UI glitz side of Silverlight and the audience responded very where.&amp;#160; Rather than going through a bunch of slides I created an application from scratch that does read\write data access via WCF and LINQ as well as local storage via Isolated storage&amp;#160; Here is part of the flow I used:&amp;#160;&amp;#160; &lt;a href="http://blogs.msdn.com/brada/archive/2008/06/26/data-focused-silverlight-demo.aspx"&gt;http://blogs.msdn.com/brada/archive/2008/06/26/data-focused-silverlight-demo.aspx&lt;/a&gt;&amp;#160; and &lt;a href="http://blogs.msdn.com/brada/archive/2008/06/23/using-asp-net-authentication-in-a-web-service-with-silverlight.aspx"&gt;http://blogs.msdn.com/brada/archive/2008/06/23/using-asp-net-authentication-in-a-web-service-with-silverlight.aspx&lt;/a&gt;.&amp;#160; Thanks &lt;a href="http://blogs.msdn.com/scmorris/"&gt;Scott Morrison&lt;/a&gt; for your help with this!&amp;#160;&amp;#160; Here are the &lt;a href="http://brad_abrams.members.winisp.net/Projects/SouthAfrica08/Silverlight2-RIA.pptx"&gt;slides&lt;/a&gt; I used to kick it off.&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;u&gt;Ajax:&lt;/u&gt;&lt;/p&gt;  &lt;p&gt;I had a great time doing this ajax demo&amp;#8230; It was basically a reprise of &lt;a href="http://blogs.msdn.com/brada/archive/2008/03/06/mix08-session-overview-building-great-ajax-applications-from-scratch-using-asp-net-3-5-and-visual-studio-2008.aspx"&gt;my Mix 08 talk&lt;/a&gt; which is an all demo talk..&amp;#160; I could tell people really loved it!&amp;#160; Because I had just come back from Safari, &lt;a href="http://blogs.msdn.com/brada/archive/2008/06/30/updated-talk-building-great-ajax-applications-from-scratch-using-asp-net-3-5-and-visual-studio-2008.aspx"&gt;I re-themed it with my pictures&lt;/a&gt;.&amp;#160;&amp;#160;&amp;#160; &lt;/p&gt;  &lt;p&gt;&lt;a href="http://blogs.msdn.com/blogfiles/brada/WindowsLiveWriter/LovingtheSouthAfricanDeveloperCommunity_8F7A/clip_image005_2.gif"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="184" alt="clip_image005" src="http://blogs.msdn.com/blogfiles/brada/WindowsLiveWriter/LovingtheSouthAfricanDeveloperCommunity_8F7A/clip_image005_thumb.gif" width="244" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;   &lt;br /&gt;&lt;u&gt;MVC:&lt;/u&gt;&lt;/p&gt;  &lt;p&gt;I split the last talk on the &amp;#8220;future of ASP.NET&amp;#8221; into two parts&amp;#8230; Part 1 was on MVC.&amp;#160;&amp;#160; The &amp;#8220;no viewstate&amp;#8221; and the &amp;#8220;IDs not mangled&amp;#8221; were winners!&amp;#160;&amp;#160;&amp;#160; Folks generally seemed to get that they did not have to move to ASP.NET MVC.&amp;#160; The two consistent big asks where for better\more ajax support and some sort of reusable component support.&amp;#160; I think we are well on our way to address both of those.&amp;#160; Here are the&amp;#160;&amp;#160;&amp;#160; &lt;a href="http://brad_abrams.members.winisp.net/Projects/SouthAfrica08/MVC-Presentation.pptx"&gt;slides&lt;/a&gt; and &lt;a href="http://brad_abrams.members.winisp.net/Projects/SouthAfrica08/MVCNorthwindDemo.zip"&gt;demo&lt;/a&gt; I used.    &lt;br /&gt;&lt;/p&gt;  &lt;p&gt;&lt;u&gt;Dynamic Data:&lt;/u&gt;&lt;/p&gt;  &lt;p&gt;This talk was a real crowd pleaser.&amp;#160; I started off by showing how to change an existing site into being Dynamic Data aware and this really drove home the point about the value Dynamic Data adds.&amp;#160; &lt;a href="http://blogs.msdn.com/brada/archive/2008/06/20/asp-net-dynamicdata-an-introductory-demo.aspx"&gt;http://blogs.msdn.com/brada/archive/2008/06/20/asp-net-dynamicdata-an-introductory-demo.aspx&lt;/a&gt; .&amp;#160; By this point &amp;#189; the room was already sold&amp;#8230; but there was much more.&amp;#160; I then used the wizard to show off all the customization capabilities of&amp;#160; DD.&amp;#160; That pulled&amp;#160; the rest of them in.&amp;#160;&amp;#160; &lt;a href="http://blogs.msdn.com/brada/archive/2008/06/27/asp-net-dynamic-data-customizing-the-ui.aspx"&gt;http://blogs.msdn.com/brada/archive/2008/06/27/asp-net-dynamic-data-customizing-the-ui.aspx&lt;/a&gt; .&amp;#160; The questions here were all about support for different data sources. NHibernate, plain old objects, etc.&amp;#160;&amp;#160;&amp;#160; I think we have a great story here and I wish I would have had time to demo it.&amp;#160;&amp;#160; Here are the &lt;a href="http://brad_abrams.members.winisp.net/Projects/SouthAfrica08/DynamicDataReMixSA.pptx"&gt;slides&lt;/a&gt; I used&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;&lt;u&gt;Closing&lt;/u&gt;&lt;/p&gt;  &lt;p&gt;I had a little fun to close out the event.&amp;#160; Through the day I had been throwing out giveaways (t-shirts\balls\hats) whenever someone asked a question or pointed out a bug I had made (there were a few!).&amp;#160;&amp;#160; But I was left with a big Microsoft keyboard that I didn&amp;#8217;t want to through.&amp;#160; So I ask for a volunteer to come up and code something on stage like I had done all day!&amp;#160; I chose creating a LINQ model over top of Northwind as I did that like 5 different times during the event so I thought it was fair.&amp;#160;&amp;#160;&amp;#160; The victims.. ah, I mean volunteers at the Cape Town and Johannesburg events did well!&amp;#160; They started off kind of shaky and made me nervous, but they got in and nailed it!&amp;#160; It was a good fun time for all as the audience &amp;#8220;helped&amp;#8221; out a bit by yelling suggestions.&amp;#160; One guy even said &amp;#8220;you owe me &amp;#189; that keyboard&amp;#8221; when he finally go it ;-).&amp;#160; Oh, and some folks asked me about the zooming tool I used to zoom in on the screen.&amp;#160; It is called &lt;a href="http://technet.microsoft.com/en-us/sysinternals/bb897434.aspx"&gt;ZoomIt&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://blogs.msdn.com/blogfiles/brada/WindowsLiveWriter/LovingtheSouthAfricanDeveloperCommunity_8F7A/clip_image006_2.jpg"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="310" alt="clip_image006" src="http://blogs.msdn.com/blogfiles/brada/WindowsLiveWriter/LovingtheSouthAfricanDeveloperCommunity_8F7A/clip_image006_thumb.jpg" width="246" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;Here are a few blog posts I have already seen from the event..&lt;/p&gt;  &lt;p&gt;&lt;a href="http://liamb.com/2008/06/23/brad-abrams-visits-south-africa/"&gt;http://liamb.com/2008/06/23/brad-abrams-visits-south-africa/&lt;/a&gt;    &lt;br /&gt;&lt;a href="http://metallemon.blogspot.com/2008/06/mix-essentials-2008.html"&gt;http://metallemon.blogspot.com/2008/06/mix-essentials-2008.html&lt;/a&gt;    &lt;br /&gt;&lt;a href="http://www.fremus.co.za/blog/2008/06/first-experiences-with-visual-studio-2008/"&gt;http://www.fremus.co.za/blog/2008/06/first-experiences-with-visual-studio-2008/&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;I'd love to hear your feedback\thoughts on this... and if you blogged on the event please let me know and I will add your link here.&amp;#160;&amp;#160; &lt;/p&gt;  &lt;p&gt;Oh, and I did spent a few days on Safari at Mala Mala adjacent to Krueger national park.&amp;#160;&amp;#160; I was amazed at the big game viewing... I highly recommend it.&amp;#160; Here are &lt;a href="http://flickr.com/photos/25954536@N04/"&gt;some photos&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://blogs.msdn.com/blogfiles/brada/WindowsLiveWriter/LovingtheSouthAfricanDeveloperCommunity_8F7A/image_2.png"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="201" alt="image" src="http://blogs.msdn.com/blogfiles/brada/WindowsLiveWriter/LovingtheSouthAfricanDeveloperCommunity_8F7A/image_thumb.png" width="244" border="0" /&gt;&lt;/a&gt; &lt;a href="http://blogs.msdn.com/blogfiles/brada/WindowsLiveWriter/LovingtheSouthAfricanDeveloperCommunity_8F7A/image_4.png"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="107" alt="image" src="http://blogs.msdn.com/blogfiles/brada/WindowsLiveWriter/LovingtheSouthAfricanDeveloperCommunity_8F7A/image_thumb_1.png" width="244" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=8677861" width="1" height="1"&gt;</description>
      <pubDate>Tue, 01 Jul 2008 16:00:05 CDT</pubDate>
      <guid isPermaLink="true">91d46819-8472-40ad-a661-2c78acb4018c:8677861</guid>
      <dc:creator>Brad Abrams</dc:creator>
    </item>
    <item>
      <title>Public Speaking - Great Beginnings</title>
      <link>http://www.agileitx.com/blog_detail.jsp?rssItemId=124212&amp;utm_source=blogitem&amp;utm_medium=rss&amp;utm_campaign=blogrss</link>
      <description>&lt;p&gt;&#xD;
          &lt;a title="The Starting Line" href="http://www.flickr.com/photos/51532760@N00/2095494955/"&gt;&#xD;
            &lt;img class="flickr" alt="The Starting Line" hspace="5" src="http://static.flickr.com/2044/2095494955_f7f9ef9754_m.jpg" align="left" vspace="5" border="0"&gt;&lt;/img&gt;&#xD;
          &lt;/a&gt;I'm&#xD;
giving a talk next week at the Microsoft Worldwide Partner Conference (WPC). I happen&#xD;
to be in the fun position where I'm directly following the keynote. I've sat in on&#xD;
content reviews and keynote writing sessions so I know the messaging from that talk&#xD;
and the overlap that there will be with my session. &#xD;
&lt;/p&gt;&#xD;
        &lt;p&gt;&#xD;
One of the services that they offer to their speakers is a speech coach. It's the&#xD;
first time that I've worked with one one on one. I sat through a class offered by&#xD;
a different conference a number of years back. It was fairly useless so I didn't have&#xD;
high hopes for another speaker coach. Boy was I wrong. &#xD;
&lt;/p&gt;&#xD;
        &lt;p&gt;&#xD;
I met with Cathy Banks of &lt;a href="http://www.communicationpowerinc.com/" target="_blank"&gt;Communication&#xD;
Power, Inc&lt;/a&gt;. I cockily told her that I have spoken at hundreds of conferences and&#xD;
typically get 8s or better on my evals. Turns out I'm not the only speaker out there&#xD;
that's had Cathy change the way that they speak - here's a post by &lt;a href="http://www.commoncraft.com/blog" target="_blank"&gt;Lee&#xD;
Lefever&lt;/a&gt; talking about his &lt;a href="http://www.commoncraft.com/10-lessons-my-speaking-coach" target="_blank"&gt;10&#xD;
lessons learned from his speaking coach&lt;/a&gt;.&#xD;
&lt;/p&gt;&#xD;
        &lt;p&gt;&#xD;
I talked to her about the fact that it often takes me 3-4 times to really nail the&#xD;
ending on a talk. It's been a constant problem that I've had on how to end a talk.&#xD;
I often run over time because I'm rambling trying to figure out how to finish a session. &#xD;
&lt;/p&gt;&#xD;
        &lt;p&gt;&#xD;
Cathy, to my surprise, insisted that we start on the opening. I'm not usually concerned&#xD;
about the opening. I have a fairly casual style that sets the audience at ease and&#xD;
gets their attention. &#xD;
&lt;/p&gt;&#xD;
        &lt;p&gt;&#xD;
It was brutal. She had me do the opening, asked me how I thought it went. I was relatively&#xD;
pleased. Then asked me what the point of the talk was because she obviously didn't&#xD;
get it from my opening. I ran through the highlights of the talk and pointed out the&#xD;
top level message that I was targeting. &#xD;
&lt;/p&gt;&#xD;
        &lt;p&gt;&#xD;
All of the sudden, I saw the issue with the opening that she was pointing out. I was&#xD;
rambling through the opening without clearly framing the overall message for the talk.&#xD;
My opening was entertaining, but really didn't succinctly tell the audience what they&#xD;
needed to get out of this session. That's the hook that gets the audience to listen&#xD;
for the rest of the session. &#xD;
&lt;/p&gt;&#xD;
        &lt;p&gt;&#xD;
Then she asked me what I knew about the crowd. I felt good about this because I knew&#xD;
the target demographics pretty well and was able to talk to that. It's mostly going&#xD;
to be business level folk in the room. These are management, business-development&#xD;
folk, business owners and so on at this conference. But then she turns it on me asking&#xD;
how my overall message related to this crowd. My message was far to technical and&#xD;
this audience really doesn't care. I need to give them the couple of technical sound&#xD;
bytes but really hit on how they are going to make money from all of this. &#xD;
&lt;/p&gt;&#xD;
        &lt;p&gt;&#xD;
With all of that in mind, I tried my opening again. It felt better but was still a&#xD;
little off. &#xD;
&lt;/p&gt;&#xD;
        &lt;p&gt;&#xD;
She took a crack at an opening off the top of her head. And nailed it. As I parsed&#xD;
out what she had just said, I thought through my version of the opening. It was funny&#xD;
but was not nearly crisp enough. It wasn't well defined as an opening. It was talked&#xD;
to the crowd now but still didn't hit the 2-3 high level points that I was going to&#xD;
dive deep in for the rest of the session. &#xD;
&lt;/p&gt;&#xD;
        &lt;p&gt;&#xD;
I tried it again. And again. And again. Somewhere in here, she started goading me&#xD;
on saying, "Come on Josh! How'd you get through those hundreds of conferences? Tell&#xD;
me that story. I know you're a story teller so tell me the story!". Yes it stung,&#xD;
but wow it worked. &#xD;
&lt;/p&gt;&#xD;
        &lt;p&gt;&#xD;
Things started to crisp up. I started off my opening with a continuation of a story&#xD;
from the previous talk. I carry over some of the language from the keynote as well.&#xD;
This will help with continuity and really draw the connection between the keynote&#xD;
and my session. I hit the two points that I wanted to hit, gave a solid hook, did&#xD;
all that in a humorous manner and in less than a minute. WOW! I was thrilled.�� &#xD;
&lt;/p&gt;&#xD;
        &lt;p&gt;&#xD;
Now that I had my opening down, Cathy asked me to jump to the closing. The first one&#xD;
was a little rough. Then she asked me to run through the opening again and jump immediately&#xD;
to the closing. That was killer. I got a fairly solid closing on the second try. Two&#xD;
more refinement rounds and I was set. I carried the language through from the opening,&#xD;
tied off the ends on the two points that I wanted to get across in the session and&#xD;
had a solid call to action. &#xD;
&lt;/p&gt;&#xD;
        &lt;p&gt;&#xD;
As Cathly pointed out to me VERY clearly, the reason that I couldn't close a session&#xD;
crisply was that I didn't have a good opening. &lt;em&gt;With a good opening comes a good&#xD;
closing.&lt;/em&gt;&lt;/p&gt;&#xD;
        &lt;p&gt;&#xD;
All that's left is the stuffing in the middle. &#xD;
&lt;/p&gt;&#xD;
        &lt;p&gt;&#xD;
          &lt;strong&gt;Keys to a good opening&lt;/strong&gt;&#xD;
        &lt;/p&gt;&#xD;
        &lt;p&gt;&#xD;
          &lt;em&gt;Understand the audience&lt;/em&gt;. This helps you target the content at the right level. &#xD;
&lt;/p&gt;&#xD;
        &lt;p&gt;&#xD;
          &lt;em&gt;Understand the message&lt;/em&gt;. I put this second because there are a lot of messages&#xD;
that don't apply to all audiences. But this is a critical part of the talk. Many talks&#xD;
that I see don't really have a "message". They just want to demo a technique or something.&#xD;
The best talks had a solid call to action and are trying to motivate people to do&#xD;
something. &#xD;
&lt;/p&gt;&#xD;
        &lt;p&gt;&#xD;
          &lt;em&gt;Set up the language&lt;/em&gt; for the rest of the talk. &#xD;
&lt;/p&gt;&#xD;
        &lt;p&gt;&#xD;
          &lt;em&gt;Write it out&lt;/em&gt;. &lt;a href="http://apolloideas.com" target="_blank"&gt;Apollo Ideas&lt;/a&gt; has&#xD;
a post called &lt;a href="http://apolloideas.com/blog/archives/108" target="_blank"&gt;Prepare&#xD;
yourself&lt;/a&gt; that discusses the different levels of preparation you can have for a&#xD;
great presentation. I talked about it a little in my post &lt;a href="http://www.joshholmes.com/2008/05/29/PrepareYourselfToGiveAGreatTalk.aspx" target="_blank"&gt;Prepare&#xD;
Yourself To Give a Great Talk&lt;/a&gt;. This is especially true for the opening and closing&#xD;
of your session. These are the times that you are in the most control of your session. &#xD;
&lt;/p&gt;&#xD;
        &lt;p&gt;&#xD;
          &lt;em&gt;Don't worry about establishing credibility&lt;/em&gt;. Because you're onstage, you already&#xD;
have the credibility that you need. The conference has given you that. It's your credibility&#xD;
to lose, not gain. Nobody cares how smart you think you are. They care what you're&#xD;
going to be talking about and the points that you're goign to be making. &lt;a href="http://www.designthinkingdigest.com" target="_blank"&gt;Chris&#xD;
Bernard&lt;/a&gt; usually does his whole opening and then gives people his contact information.&#xD;
I'm going to steal this idea. &#xD;
&lt;/p&gt;&#xD;
        &lt;p&gt;&#xD;
          &lt;em&gt;Have fun&lt;/em&gt;. If you're not having fun, the audience won't either. &#xD;
&lt;/p&gt;&#xD;
        &lt;p&gt;&#xD;
          &lt;strong&gt;More reading&lt;/strong&gt;&#xD;
        &lt;/p&gt;&#xD;
        &lt;li&gt;&#xD;
Kathy Sierra: &lt;a href="http://headrush.typepad.com/creating_passionate_users/2006/10/better_beginnin.html"&gt;Better&#xD;
Beginnings: How to Start a Presentation, Book, Article &lt;/a&gt;&lt;/li&gt;&#xD;
        &lt;li&gt;&#xD;
John Kinde: &lt;a href="http://humorpower.com/art-winningspeech.html" target="_blank"&gt;Winning&#xD;
Your Speech At the Starting Line&lt;/a&gt;&lt;/li&gt;&#xD;
        &lt;p&gt;&#xD;
        &lt;/p&gt;&#xD;
        &lt;div class="wlWriterSmartContent" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:c384184d-92a9-499e-9313-bdd2b60e4a4d" style="padding-right: 0px; display: inline; padding-left: 0px; padding-bottom: 0px; margin: 0px; padding-top: 0px"&gt;Technorati&#xD;
Tags: &lt;a href="http://technorati.com/tags/Speaking" rel="tag"&gt;Speaking&lt;/a&gt;,&lt;a href="http://technorati.com/tags/Opening" rel="tag"&gt;Opening&lt;/a&gt;&lt;/div&gt;&#xD;
        &lt;img width="0" height="0" src="http://www.joshholmes.com/aggbug.ashx?id=20c701cf-d633-465a-95d2-ad53b709af20"&gt;&lt;/img&gt;&#xD;
      &lt;div class="feedflare"&gt;
&lt;a href="http://rss.joshholmes.com/~f/JoshHolmes?a=WNjbOJ"&gt;&lt;img src="http://rss.joshholmes.com/~f/JoshHolmes?i=WNjbOJ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://rss.joshholmes.com/~f/JoshHolmes?a=uFSAvj"&gt;&lt;img src="http://rss.joshholmes.com/~f/JoshHolmes?i=uFSAvj" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://rss.joshholmes.com/~f/JoshHolmes?a=0ve4Aj"&gt;&lt;img src="http://rss.joshholmes.com/~f/JoshHolmes?i=0ve4Aj" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://rss.joshholmes.com/~f/JoshHolmes?a=kDAa3J"&gt;&lt;img src="http://rss.joshholmes.com/~f/JoshHolmes?i=kDAa3J" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://rss.joshholmes.com/~f/JoshHolmes?a=8i00nj"&gt;&lt;img src="http://rss.joshholmes.com/~f/JoshHolmes?i=8i00nj" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://rss.joshholmes.com/~r/JoshHolmes/~4/324151644" height="1" width="1"/&gt;</description>
      <pubDate>Tue, 01 Jul 2008 13:00:02 CDT</pubDate>
      <guid isPermaLink="true">http://www.joshholmes.com/PermaLink,guid,20c701cf-d633-465a-95d2-ad53b709af20.aspx</guid>
      <dc:creator>Josh Holmes</dc:creator>
    </item>
    <item>
      <title>James Gosling</title>
      <link>http://www.agileitx.com/blog_detail.jsp?rssItemId=124210&amp;utm_source=blogitem&amp;utm_medium=rss&amp;utm_campaign=blogrss</link>
      <description>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp3.blogger.com/_HOfY71whEUc/SGpCDIAv2aI/AAAAAAAAAF0/lsPeuA08rqw/s1600-h/jg2.jpg"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer;" src="http://bp3.blogger.com/_HOfY71whEUc/SGpCDIAv2aI/AAAAAAAAAF0/lsPeuA08rqw/s320/jg2.jpg" alt="" id="BLOGGER_PHOTO_ID_5218055739719801250" border="0" /&gt;&lt;/a&gt;I own most of my success to the Java Platform and therefor, in my opinion, to the team that developed Java - including James Gosling. I've always been in awe of James Gosling mostly because he is deemed "the father of Java" and has been the top guy in the Java industry for as long as I can remember (stretching back to my introduction to Java in 1995).&lt;br /&gt;&lt;br /&gt;My admiration for him is based on that fact that he invented a platform that was not only interesting and exciting but a platform that is perhaps the  most successful programming platform in history. There are more Java software developers and more Java software than just about anything else - the possible exception might be C/C++.&lt;br /&gt;&lt;br /&gt;There is a pretty good article about James Gosling and the future of Sun Microsystems, called &lt;a href="http://www.canadianbusiness.com/technology/companies/article.jsp?content=20080611_47923_47923&amp;amp;page=1"&gt;Java Computing: Second Cup?&lt;/a&gt;,  which was published in Canadian Business magazine - its an example of great reporting which in my opinion is missing from technical press these days.&lt;br /&gt;&lt;br /&gt;I always wanted to write an accurate but populist account of the history of Java, but I'll probably never get around to do it. Computing history is fascinating to me. I hope someone  writes a book about the invention and subsequent ups and downs of the Java platform and Sun Microsystems.  It would be fascinating.</description>
      <pubDate>Tue, 01 Jul 2008 11:00:02 CDT</pubDate>
      <guid isPermaLink="true">tag:blogger.com,1999:blog-7867849070722123199.post-8318636506417342725</guid>
      <dc:creator>Richard Monson-Haefel</dc:creator>
    </item>
    <item>
      <title>Testing Anti-Patterns: Overspecification</title>
      <link>http://www.agileitx.com/blog_detail.jsp?rssItemId=124182&amp;utm_source=blogitem&amp;utm_medium=rss&amp;utm_campaign=blogrss</link>
      <description>&lt;p&gt;Tests increasingly serve multiple roles in today&amp;#8217;s projects.  They help us design APIs through test-driven development.  They provide confidence that new changes aren&amp;#8217;t breaking existing functionality.  They offer an executable specification of the application.  But can we ever get to a point where we have too much testing?  &lt;/p&gt;

&lt;h2&gt;Enough is Enough&lt;/h2&gt;

&lt;p&gt;Consider the following test that you might come across in an application with a less-than-ideal test suite.  (We&amp;#8217;ll pick on some badly-written Rails code for this example, but the ideas we&amp;#8217;ll discuss are certainly not unique to just Rails or Ruby or even just to dynamic languages.  Unfortunately, these problems are quite universal.)&lt;/p&gt;

&lt;div class="dean_ch" style="white-space: wrap;"&gt;&lt;ol&gt;&lt;li class="li1"&gt;&lt;div class="de1"&gt;&lt;span class="kw3"&gt;require&lt;/span&gt; &lt;span class="kw4"&gt;File&lt;/span&gt;.&lt;span class="me1"&gt;dirname&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="kw2"&gt;__FILE__&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt; + &lt;span class="st0"&gt;&amp;#8216;/../test_helper&amp;#8217;&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li class="li1"&gt;&lt;div class="de1"&gt;&amp;nbsp;&lt;/div&gt;&lt;/li&gt;
&lt;li class="li1"&gt;&lt;div class="de1"&gt;&lt;span class="kw1"&gt;class&lt;/span&gt; ProductsControllerTest &amp;lt; &lt;span class="re2"&gt;ActionController::TestCase&lt;/span&gt; &lt;/div&gt;&lt;/li&gt;
&lt;li class="li1"&gt;&lt;div class="de1"&gt;&amp;nbsp; &lt;span class="kw1"&gt;def&lt;/span&gt; test_something&lt;/div&gt;&lt;/li&gt;
&lt;li class="li2"&gt;&lt;div class="de2"&gt;&amp;nbsp; &amp;nbsp; product = Product.&lt;span class="me1"&gt;create&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re3"&gt;:name&lt;/span&gt; =&amp;gt; &lt;span class="st0"&gt;&amp;quot;Frisbee&amp;quot;&lt;/span&gt;, &lt;span class="re3"&gt;:price&lt;/span&gt; =&amp;gt; &lt;span class="nu0"&gt;5.00&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li class="li1"&gt;&lt;div class="de1"&gt;&amp;nbsp; &amp;nbsp; get &lt;span class="re3"&gt;:show&lt;/span&gt;, &lt;span class="re3"&gt;:id&lt;/span&gt; =&amp;gt; product.&lt;span class="me1"&gt;id&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li class="li1"&gt;&lt;div class="de1"&gt;&amp;nbsp; &amp;nbsp; assert_response &lt;span class="re3"&gt;:success&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li class="li1"&gt;&lt;div class="de1"&gt;&amp;nbsp; &amp;nbsp; product = assigns&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re3"&gt;:product&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li class="li1"&gt;&lt;div class="de1"&gt;&amp;nbsp; &amp;nbsp; assert_not_nil product&lt;/div&gt;&lt;/li&gt;
&lt;li class="li2"&gt;&lt;div class="de2"&gt;&amp;nbsp; &amp;nbsp; assert product.&lt;span class="me1"&gt;valid&lt;/span&gt;?&lt;/div&gt;&lt;/li&gt;
&lt;li class="li1"&gt;&lt;div class="de1"&gt;&amp;nbsp; &amp;nbsp; assert product.&lt;span class="me1"&gt;name&lt;/span&gt; == &lt;span class="st0"&gt;&amp;quot;Frisbee&amp;quot;&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li class="li1"&gt;&lt;div class="de1"&gt;&amp;nbsp; &amp;nbsp; assert product.&lt;span class="me1"&gt;price&lt;/span&gt; == &lt;span class="nu0"&gt;5.00&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li class="li1"&gt;&lt;div class="de1"&gt;&amp;nbsp; &lt;span class="kw1"&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li class="li1"&gt;&lt;div class="de1"&gt;&lt;span class="kw1"&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;

&lt;p&gt;&lt;br/&gt;&lt;/p&gt;

&lt;p&gt;Whoa!  That sure is a lot going on in a single test case. What exactly is it that we&amp;#8217;re trying to test here?  Let&amp;#8217;s take a step back and see if we can figure it out. [1]&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Line 5&lt;/strong&gt; - We start off by creating a new product.  We give the product a name and a price and call &lt;code&gt;#create&lt;/code&gt;.  Since we&amp;#8217;re in a Rails app, without looking elsewhere in the code, we can make an educated guess that &lt;code&gt;Product&lt;/code&gt; is an &lt;code&gt;ActiveRecord&lt;/code&gt; subclass and that calling &lt;code&gt;#create&lt;/code&gt; will persist the product to the database.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Line 6&lt;/strong&gt; - We send an HTTP GET request to a &lt;code&gt;#show&lt;/code&gt; method and pass along the ID of the product we just created.  Since we&amp;#8217;re in &lt;code&gt;ProductsControllerTest&lt;/code&gt;, we can safely infer that we&amp;#8217;re calling the &lt;code&gt;#show&lt;/code&gt; action in the &lt;code&gt;ProductsController&lt;/code&gt; class.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Line 7&lt;/strong&gt; - We assert that the controller action responded with HTTP status code 200 (i.e., success).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lines 8-9&lt;/strong&gt; - We look for an object stored in the &lt;code&gt;assigns&lt;/code&gt; hash (i.e., the hash of variables that will be available to the view template) with a key of &lt;code&gt;:product&lt;/code&gt; and we assert that it&amp;#8217;s not nil.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Line 10&lt;/strong&gt; - We verify that the product object satisfies all the product validation rules (though we can&amp;#8217;t know what those rules are without taking a look at the &lt;code&gt;Product&lt;/code&gt; class).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lines 11-12&lt;/strong&gt; - We assert that the attribute values we provided when creating the product (in line 5) match the attribute values stored in the object we fetched from the &lt;code&gt;assigns&lt;/code&gt; hash.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a class called &lt;code&gt;ProductsControllerTest&lt;/code&gt;, it sure feels like we&amp;#8217;re doing a fair bit more than just testing the code we&amp;#8217;d expect to find in &lt;code&gt;ProductsController&lt;/code&gt; (assuming &lt;code&gt;ProductsController&lt;/code&gt; conforms to the traditional responsibilities of a controller).  Let&amp;#8217;s take a look at the controller code to better understand exactly what it is that we&amp;#8217;re testing.&lt;/p&gt;

&lt;div class="dean_ch" style="white-space: wrap;"&gt;&lt;ol&gt;&lt;li class="li1"&gt;&lt;div class="de1"&gt;&lt;span class="kw1"&gt;class&lt;/span&gt; ProductsController &amp;lt; ApplicationController&lt;/div&gt;&lt;/li&gt;
&lt;li class="li1"&gt;&lt;div class="de1"&gt;&amp;nbsp; &lt;span class="kw1"&gt;def&lt;/span&gt; show&lt;/div&gt;&lt;/li&gt;
&lt;li class="li1"&gt;&lt;div class="de1"&gt;&amp;nbsp; &amp;nbsp; &lt;span class="re1"&gt;@product&lt;/span&gt; = Product.&lt;span class="me1"&gt;find&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;params&lt;span class="br0"&gt;&amp;#91;&lt;/span&gt;&lt;span class="re3"&gt;:id&lt;/span&gt;&lt;span class="br0"&gt;&amp;#93;&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li class="li1"&gt;&lt;div class="de1"&gt;&amp;nbsp; &lt;span class="kw1"&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li class="li2"&gt;&lt;div class="de2"&gt;&lt;span class="kw1"&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;

&lt;p&gt;&lt;br/&gt;&lt;/p&gt;

&lt;p&gt;It turns out that our controller code is notably simpler than our test case would have led us to believe.  The &lt;code&gt;#show&lt;/code&gt; action does indeed stick to the expected behavior of a controller, and in this case, it&amp;#8217;s able to provide that behavior in a single line of code (despite the 8 lines of test code currently employed above).  That single line satisfies all of the expectations of the &lt;code&gt;#show&lt;/code&gt; action: look up the product with the given ID and place the product object in an instance variable for use by the view.&lt;/p&gt;

&lt;p&gt;If the &lt;code&gt;#show&lt;/code&gt; action isn&amp;#8217;t responsible for knowing how to successfully save a new product record into the database, why does our test case attempt to provide the data for creating a new product?  What will happen to our test case when someone adds a new validation rule requiring that all products also include a quantity attribute?  Unfortunately, the test as it&amp;#8217;s currently written will break.  And while a failing test is often a welcome warning that our changes have inadvertently broken a part of our application, it&amp;#8217;s far from desirable for a &lt;em&gt;model&lt;/em&gt;-level rule related to creating new records to cause a failure in a &lt;em&gt;controller&lt;/em&gt; test related simply to displaying existing records.  In short, we&amp;#8217;ve given our test case too much responsibility, and in doing so, we&amp;#8217;ve made it fragile.&lt;/p&gt;

&lt;p&gt;We can see from the &lt;code&gt;#show&lt;/code&gt; action above that not only is the controller not responsible for knowing how to create a valid product, it&amp;#8217;s also not responsible for ensuring that a product&amp;#8217;s attributes are properly populated when the record is read from the database.  However, if we take another look at the last few lines of the test case, we might think otherwise.  That brings us to the second problem with this particular test: it&amp;#8217;s a rotten source of documentation for the code being tested.  As we increasingly move toward tests as &lt;em&gt;specifications&lt;/em&gt; of our application&amp;#8217;s behavior, it&amp;#8217;s vital that those specifications clearly communicate the expected behavior.  As it&amp;#8217;s currently implemented, the &lt;strong&gt;overspecification&lt;/strong&gt; in this test case leaves the reader having to do way too much work to figure out the true expectations of the code being tested.&lt;/p&gt;

&lt;h2&gt;Communicate Essence&lt;/h2&gt;

&lt;p&gt;Let&amp;#8217;s take another pass at writing a test for the &lt;code&gt;#show&lt;/code&gt; action, this time with an eye toward removing the fragility of the previous test case and increasing the value of the test as a specification.&lt;/p&gt;

&lt;div class="dean_ch" style="white-space: wrap;"&gt;&lt;ol&gt;&lt;li class="li1"&gt;&lt;div class="de1"&gt;&lt;span class="kw3"&gt;require&lt;/span&gt; &lt;span class="kw4"&gt;File&lt;/span&gt;.&lt;span class="me1"&gt;dirname&lt;/span&gt;&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="kw2"&gt;__FILE__&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt; + &lt;span class="st0"&gt;&amp;#8216;/../test_helper&amp;#8217;&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li class="li1"&gt;&lt;div class="de1"&gt;&amp;nbsp;&lt;/div&gt;&lt;/li&gt;
&lt;li class="li1"&gt;&lt;div class="de1"&gt;&lt;span class="kw1"&gt;class&lt;/span&gt; ProductsControllerTest &amp;lt; &lt;span class="re2"&gt;ActionController::TestCase&lt;/span&gt; &lt;/div&gt;&lt;/li&gt;
&lt;li class="li1"&gt;&lt;div class="de1"&gt;&amp;nbsp; &lt;span class="kw1"&gt;def&lt;/span&gt; test_should_show_product &lt;/div&gt;&lt;/li&gt;
&lt;li class="li2"&gt;&lt;div class="de2"&gt;&amp;nbsp; &amp;nbsp; product = create_product &lt;/div&gt;&lt;/li&gt;
&lt;li class="li1"&gt;&lt;div class="de1"&gt;&amp;nbsp; &amp;nbsp; get &lt;span class="re3"&gt;:show&lt;/span&gt;, &lt;span class="re3"&gt;:id&lt;/span&gt; =&amp;gt; product.&lt;span class="me1"&gt;id&lt;/span&gt; &lt;/div&gt;&lt;/li&gt;
&lt;li class="li1"&gt;&lt;div class="de1"&gt;&amp;nbsp; &amp;nbsp; assert_response &lt;span class="re3"&gt;:success&lt;/span&gt; &lt;/div&gt;&lt;/li&gt;
&lt;li class="li1"&gt;&lt;div class="de1"&gt;&amp;nbsp; &amp;nbsp; assert_equal product, assigns&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;&lt;span class="re3"&gt;:product&lt;/span&gt;&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt; &lt;/div&gt;&lt;/li&gt;
&lt;li class="li1"&gt;&lt;div class="de1"&gt;&amp;nbsp; &lt;span class="kw1"&gt;end&lt;/span&gt; &lt;/div&gt;&lt;/li&gt;
&lt;li class="li2"&gt;&lt;div class="de2"&gt;&lt;span class="kw1"&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;

&lt;p&gt;&lt;br/&gt;&lt;/p&gt;

&lt;p&gt;In this implementation, we&amp;#8217;ve abstracted away the logic for creating a new product in line 5.  We&amp;#8217;ve defined a helper method for use by any and all tests in our application that have a need to create a new product.  If and when the rules for successfully creating a new product change, we&amp;#8217;ll update the &lt;code&gt;#create_product&lt;/code&gt; method, and we won&amp;#8217;t have to touch the code in &lt;code&gt;ProductsController&lt;/code&gt; or &lt;code&gt;ProductsControllerTest&lt;/code&gt; at all. [2] &lt;/p&gt;

&lt;p&gt;As for the four assertions that appeared at the end of our first attempt at this test case, we&amp;#8217;ve replaced those assertions with a single (stronger) assertion.  Where we previously asserted that the &lt;code&gt;assigns&lt;/code&gt; hash held a non-nil product, that the product was valid, and that its attributes matched the attributes used at the beginning of the test, we now simply verify that the product object in the &lt;code&gt;assigns&lt;/code&gt; hash is equal to the product object that we created at the beginning of the test.  That single line more accurately and more concisely expresses our expectation: that the product whose ID we provide in the request to the &lt;code&gt;#show&lt;/code&gt; action is the same object that&amp;#8217;s made available to the view.  &lt;/p&gt;

&lt;p&gt;By focusing our test case on the &lt;em&gt;essence&lt;/em&gt; of the code under test, we&amp;#8217;ve ended up with less test code to maintain.  And with the extraneous setup and assertions removed, the remaining test code provides a clearer and less fragile specification of the behavior being tested. [3]&lt;/p&gt;

&lt;h2&gt;May I Take Your Order, Please?&lt;/h2&gt;

&lt;p&gt;In the previous example, we &lt;em&gt;might&lt;/em&gt; have detected the overspecification by the significant mismatch between the lines of test code and the lines of production code, or seeing model-specific assertions in a controller-specific test may have caught our eye.  But, overspecification comes in more subtle forms as well.  Consider the following method provided by &lt;a href="http://api.rubyonrails.org/classes/ActiveRecord/Base.html#M001329" title="Class: ActiveRecord::Base"&gt;&lt;code&gt;ActiveRecord::Base&lt;/code&gt;&lt;/a&gt; for fetching the list of columns that hold domain-specific content from a model class in Rails.&lt;/p&gt;

&lt;div class="dean_ch" style="white-space: wrap;"&gt;&lt;ol&gt;&lt;li class="li1"&gt;&lt;div class="de1"&gt;&lt;span class="co1"&gt;# Returns an array of column objects where the primary id, all columns ending in &amp;quot;_id&amp;quot; or &amp;quot;_count&amp;quot;,&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li class="li1"&gt;&lt;div class="de1"&gt;&lt;span class="co1"&gt;# and columns used for single table inheritance have been removed.&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li class="li1"&gt;&lt;div class="de1"&gt;&lt;span class="kw1"&gt;def&lt;/span&gt; content_columns&lt;/div&gt;&lt;/li&gt;
&lt;li class="li1"&gt;&lt;div class="de1"&gt;&amp;nbsp; &lt;span class="re1"&gt;@content_columns&lt;/span&gt; ||= columns.&lt;span class="me1"&gt;reject&lt;/span&gt; &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt; |c| c.&lt;span class="me1"&gt;primary&lt;/span&gt; || c.&lt;span class="me1"&gt;name&lt;/span&gt; =~ /&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;_id|_count&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;$/ || c.&lt;span class="me1"&gt;name&lt;/span&gt; == inheritance_column &lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li class="li2"&gt;&lt;div class="de2"&gt;&lt;span class="kw1"&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;

&lt;p&gt;&lt;br/&gt;&lt;/p&gt;

&lt;p&gt;To test this method, we&amp;#8217;ll need a sample &lt;code&gt;ActiveRecord&lt;/code&gt; model class.  Let&amp;#8217;s use the &lt;code&gt;Topic&lt;/code&gt; model, which is backed by the following table.&lt;/p&gt;

&lt;div class="dean_ch" style="white-space: wrap;"&gt;&lt;ol&gt;&lt;li class="li1"&gt;&lt;div class="de1"&gt;create_table &lt;span class="re3"&gt;:topics&lt;/span&gt;, &lt;span class="re3"&gt;:force&lt;/span&gt; =&amp;gt; &lt;span class="kw2"&gt;true&lt;/span&gt; &lt;span class="kw1"&gt;do&lt;/span&gt; |t|&lt;/div&gt;&lt;/li&gt;
&lt;li class="li1"&gt;&lt;div class="de1"&gt;&amp;nbsp; t.&lt;span class="kw3"&gt;string&lt;/span&gt; &amp;nbsp; &lt;span class="re3"&gt;:title&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li class="li1"&gt;&lt;div class="de1"&gt;&amp;nbsp; t.&lt;span class="kw3"&gt;string&lt;/span&gt; &amp;nbsp; &lt;span class="re3"&gt;:author_name&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li class="li1"&gt;&lt;div class="de1"&gt;&amp;nbsp; t.&lt;span class="kw3"&gt;string&lt;/span&gt; &amp;nbsp; &lt;span class="re3"&gt;:author_email_address&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li class="li2"&gt;&lt;div class="de2"&gt;&amp;nbsp; t.&lt;span class="me1"&gt;datetime&lt;/span&gt; &lt;span class="re3"&gt;:written_on&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li class="li1"&gt;&lt;div class="de1"&gt;&amp;nbsp; t.&lt;span class="me1"&gt;time&lt;/span&gt; &amp;nbsp; &amp;nbsp; &lt;span class="re3"&gt;:bonus_time&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li class="li1"&gt;&lt;div class="de1"&gt;&amp;nbsp; t.&lt;span class="me1"&gt;date&lt;/span&gt; &amp;nbsp; &amp;nbsp; &lt;span class="re3"&gt;:last_read&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li class="li1"&gt;&lt;div class="de1"&gt;&amp;nbsp; t.&lt;span class="me1"&gt;text&lt;/span&gt; &amp;nbsp; &amp;nbsp; &lt;span class="re3"&gt;:content&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li class="li1"&gt;&lt;div class="de1"&gt;&amp;nbsp; t.&lt;span class="me1"&gt;boolean&lt;/span&gt; &amp;nbsp;&lt;span class="re3"&gt;:approved&lt;/span&gt;, &lt;span class="re3"&gt;:default&lt;/span&gt; =&amp;gt; &lt;span class="kw2"&gt;true&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li class="li2"&gt;&lt;div class="de2"&gt;&amp;nbsp; t.&lt;span class="kw3"&gt;integer&lt;/span&gt; &amp;nbsp;&lt;span class="re3"&gt;:replies_count&lt;/span&gt;, &lt;span class="re3"&gt;:default&lt;/span&gt; =&amp;gt; &lt;span class="nu0"&gt;0&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li class="li1"&gt;&lt;div class="de1"&gt;&amp;nbsp; t.&lt;span class="kw3"&gt;integer&lt;/span&gt; &amp;nbsp;&lt;span class="re3"&gt;:parent_id&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li class="li1"&gt;&lt;div class="de1"&gt;&amp;nbsp; t.&lt;span class="kw3"&gt;string&lt;/span&gt; &amp;nbsp; &lt;span class="re3"&gt;:type&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li class="li1"&gt;&lt;div class="de1"&gt;&lt;span class="kw1"&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;

&lt;p&gt;&lt;br/&gt;&lt;/p&gt;

&lt;p&gt;In our first attempt at testing the &lt;code&gt;#content_columns&lt;/code&gt; method, we might come up something similar to this:&lt;/p&gt;

&lt;div class="dean_ch" style="white-space: wrap;"&gt;&lt;ol&gt;&lt;li class="li1"&gt;&lt;div class="de1"&gt;&lt;span class="co1"&gt;# (Naive) First Attempt&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li class="li1"&gt;&lt;div class="de1"&gt;&lt;span class="kw1"&gt;def&lt;/span&gt; test_content_columns&lt;/div&gt;&lt;/li&gt;
&lt;li class="li1"&gt;&lt;div class="de1"&gt;&amp;nbsp; content_columns &amp;nbsp; &amp;nbsp; &amp;nbsp;= Topic.&lt;span class="me1"&gt;content_columns&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li class="li1"&gt;&lt;div class="de1"&gt;&amp;nbsp; content_column_names = content_columns.&lt;span class="me1"&gt;map&lt;/span&gt; &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;|column| column.&lt;span class="me1"&gt;name&lt;/span&gt;&lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li class="li2"&gt;&lt;div class="de2"&gt;&amp;nbsp; assert_equal %w&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;title author_name author_email_address written_on bonus_time last_read content approved&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;, content_column_names&lt;/div&gt;&lt;/li&gt;
&lt;li class="li1"&gt;&lt;div class="de1"&gt;&lt;span class="kw1"&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;

&lt;p&gt;&lt;br/&gt;&lt;/p&gt;

&lt;p&gt;Can you spot the overspecification?  To give you a hint, compare that test to the actual test employed in the &lt;code&gt;ActiveRecord&lt;/code&gt; test suite:&lt;/p&gt;

&lt;div class="dean_ch" style="white-space: wrap;"&gt;&lt;ol&gt;&lt;li class="li1"&gt;&lt;div class="de1"&gt;&lt;span class="co1"&gt;# The Real Deal&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li class="li1"&gt;&lt;div class="de1"&gt;&lt;span class="kw1"&gt;def&lt;/span&gt; test_content_columns&lt;/div&gt;&lt;/li&gt;
&lt;li class="li1"&gt;&lt;div class="de1"&gt;&amp;nbsp; content_columns &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;= Topic.&lt;span class="me1"&gt;content_columns&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li class="li1"&gt;&lt;div class="de1"&gt;&amp;nbsp; content_column_names &amp;nbsp; = content_columns.&lt;span class="me1"&gt;map&lt;/span&gt; &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;|column| column.&lt;span class="me1"&gt;name&lt;/span&gt;&lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li class="li2"&gt;&lt;div class="de2"&gt;&amp;nbsp; assert_equal &lt;span class="nu0"&gt;8&lt;/span&gt;, content_columns.&lt;span class="me1"&gt;length&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li class="li1"&gt;&lt;div class="de1"&gt;&amp;nbsp; assert_equal %w&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;title author_name author_email_address written_on bonus_time last_read content approved&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;.&lt;span class="me1"&gt;sort&lt;/span&gt;, content_column_names.&lt;span class="me1"&gt;sort&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li class="li1"&gt;&lt;div class="de1"&gt;&lt;span class="kw1"&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;

&lt;p&gt;&lt;br/&gt;&lt;/p&gt;

&lt;p&gt;If you&amp;#8217;ve ever written a test for something that returns an ordered list of items where the actual order either doesn&amp;#8217;t matter or isn&amp;#8217;t guaranteed, you&amp;#8217;ve probably been bitten by this breed of overspecification, and there&amp;#8217;s a good chance your solution matched the solution used in the &lt;code&gt;ActiveRecord&lt;/code&gt; test code above.  Because the &lt;code&gt;#content_columns&lt;/code&gt; method doesn&amp;#8217;t guarantee that the columns will be returned in any particular order, it&amp;#8217;s inappropriate (and fragile) for our test to specify a certain order.  &lt;/p&gt;

&lt;p&gt;While the actual &lt;code&gt;ActiveRecord&lt;/code&gt; solution above works, it too could be better.  First, since we know that the two arrays in line 6 won&amp;#8217;t be equal unless they have the same length, we can safely remove the extraneous length assertion in line 5.  Second, when we see the calls to &lt;code&gt;#sort&lt;/code&gt; on line 6, we&amp;#8217;re forced to pause (if even for just a moment) to think about why it might be important to sort the two arrays.  We don&amp;#8217;t really &lt;em&gt;want&lt;/em&gt; to sort the arrays; we&amp;#8217;re just using that technique to get around the fact that order doesn&amp;#8217;t matter to us, but it does matter when Ruby compares two arrays for equality. Since we really only want to assert that the arrays have the same elements, why not do exactly that?&lt;/p&gt;

&lt;div class="dean_ch" style="white-space: wrap;"&gt;&lt;ol&gt;&lt;li class="li1"&gt;&lt;div class="de1"&gt;&lt;span class="co1"&gt;# Don&amp;#8217;t Make Me Think&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li class="li1"&gt;&lt;div class="de1"&gt;&lt;span class="kw1"&gt;def&lt;/span&gt; test_content_columns&lt;/div&gt;&lt;/li&gt;
&lt;li class="li1"&gt;&lt;div class="de1"&gt;&amp;nbsp; content_columns &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;= Topic.&lt;span class="me1"&gt;content_columns&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li class="li1"&gt;&lt;div class="de1"&gt;&amp;nbsp; content_column_names &amp;nbsp; = content_columns.&lt;span class="me1"&gt;map&lt;/span&gt; &lt;span class="br0"&gt;&amp;#123;&lt;/span&gt;|column| column.&lt;span class="me1"&gt;name&lt;/span&gt;&lt;span class="br0"&gt;&amp;#125;&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li class="li2"&gt;&lt;div class="de2"&gt;&amp;nbsp; assert_same_elements %w&lt;span class="br0"&gt;&amp;#40;&lt;/span&gt;title author_name author_email_address written_on bonus_time last_read content approved&lt;span class="br0"&gt;&amp;#41;&lt;/span&gt;, content_column_names&lt;/div&gt;&lt;/li&gt;
&lt;li class="li1"&gt;&lt;div class="de1"&gt;&lt;span class="kw1"&gt;end&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;

&lt;p&gt;&lt;br/&gt;&lt;/p&gt;

&lt;p&gt;By using an assertion like &lt;a href="http://www.thoughtbot.com/projects/shoulda" title="thoughtbot: Shoulda testing plugin"&gt;Shoulda&amp;#8217;s&lt;/a&gt; &lt;a href="http://dev.thoughtbot.com/shoulda/classes/ThoughtBot/Shoulda/General.html#M000005" title="Module: ThoughtBot::Shoulda::General"&gt;&lt;code&gt;assert_same_elements&lt;/code&gt;&lt;/a&gt; method, our test can clearly and concisely express the expected behavior.&lt;/p&gt;

&lt;h2&gt;Use It Wisely&lt;/h2&gt;

&lt;p&gt;Overspecification comes in many flavors, and the examples above in no way represent a comprehensive list.  As developers, we frequently strive to write as little code as possible to accomplish the task at hand.  When it comes to writing tests, we should very much keep that goal in mind as well.  Good tests communicate the essence of the scenario being tested and nothing more.  While I doubt a project will ever suffer from too much testing, it can certainly suffer from tests that specify too much.&lt;/p&gt;

&lt;h2&gt;Notes&lt;/h2&gt;

&lt;p&gt;[1] If this test seems absurd to you, good!  Your ability to detect this type of overspecification will serve you well.&lt;/p&gt;

&lt;p&gt;[2] The actual implementation of the &lt;code&gt;#create_product&lt;/code&gt; method isn&amp;#8217;t particularly pertinent in this discussion.  (In the Rails space, there&amp;#8217;s certainly &lt;a href="http://manuals.rubyonrails.com/read/chapter/26" title="A Guide to Testing the Rails"&gt;no&lt;/a&gt; &lt;a href="http://replacefixtures.rubyforge.org/" title="FixtureReplacement at RubyForge"&gt;shortage&lt;/a&gt; &lt;a href="http://github.com/thoughtbot/factory_girl" title="thoughtbot's factory_girl at GitHub"&gt;of&lt;/a&gt; &lt;a href="http://www.dcmanges.com/blog/38" title="Dan Manges's Blog - Rails: Fixin' Fixtures with Factory"&gt;options&lt;/a&gt;.)  &lt;/p&gt;

&lt;p&gt;[3] With all the excessive thoroughness that went into overspecifying the the &lt;em&gt;current&lt;/em&gt; behavior of the &lt;code&gt;#show&lt;/code&gt; method, is it possible that we&amp;#8217;ve been too distracted to identify other functionality that&amp;#8217;s still &lt;em&gt;missing&lt;/em&gt; from that method?  In the next post, we&amp;#8217;ll take a look at overspecification&amp;#8217;s more lethargic cousin: underspecification.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Thanks&lt;/strong&gt; to Justin Gehtland, Muness Alrubaie, and Glenn Vanderburg for reading drafts of this post.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/jasonrudolph?a=rFHvTj"&gt;&lt;img src="http://feeds.feedburner.com/~f/jasonrudolph?i=rFHvTj" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/jasonrudolph?a=Q2Z3eJ"&gt;&lt;img src="http://feeds.feedburner.com/~f/jasonrudolph?i=Q2Z3eJ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/jasonrudolph?a=wKWHaj"&gt;&lt;img src="http://feeds.feedburner.com/~f/jasonrudolph?i=wKWHaj" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/jasonrudolph/~4/323928408" height="1" width="1"/&gt;</description>
      <pubDate>Tue, 01 Jul 2008 08:00:02 CDT</pubDate>
      <guid isPermaLink="true">http://jasonrudolph.com/blog/?p=179</guid>
      <dc:creator>Jason Rudolph</dc:creator>
    </item>
  </channel>
</rss>

