<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:creativeCommons="http://backend.userland.com/creativeCommonsRssModule" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0"><channel><title>Jimmy Bogard</title><link>http://www.lostechies.com/blogs/jimmy_bogard/default.aspx</link><description>Professional driver on closed road.  Do not attempt.</description><language>en</language><generator>CommunityServer 2008.5 (Build: 30929.2835)</generator><creativeCommons:license>http://creativecommons.org/licenses/by/3.0/</creativeCommons:license><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="http://feeds.feedburner.com/GrabBagOfT" type="application/rss+xml" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com" /><item><title>A better Model Binder addendum</title><link>http://feedproxy.google.com/~r/GrabBagOfT/~3/bBS8Y_0VOmw/a-better-model-binder-addendum.aspx</link><category>ASP.NET MVC</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">bogardj</dc:creator><pubDate>Thu, 19 Nov 2009 18:31:19 PST</pubDate><guid isPermaLink="false">ded273ab-9e87-4979-8222-e4e2e46f1b46:32917</guid><description>&lt;p&gt;A while back, I wrote about a &lt;a href="http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/03/17/a-better-model-binder.aspx"&gt;ModelBinder enhancement&lt;/a&gt; we use to do arbitrary filtering on types.&amp;#160; The underlying matching algorithm only matches on one type, but we like to use layer supertypes for a lot of our domain objects, so we want to use a single model binder for every Entity or Enumeration type in our system.&lt;/p&gt;  &lt;p&gt;However, I missed an important piece in our model binding story: ModelState.&amp;#160; In the original implementation:&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public override object &lt;/span&gt;BindModel(
    &lt;span style="color: #2b91af"&gt;ControllerContext &lt;/span&gt;controllerContext, 
    &lt;span style="color: #2b91af"&gt;ModelBindingContext &lt;/span&gt;bindingContext)
{
    &lt;span style="color: blue"&gt;foreach &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;filteredModelBinder &lt;span style="color: blue"&gt;in &lt;/span&gt;_filteredModelBinders)
    {
        &lt;span style="color: blue"&gt;if &lt;/span&gt;(filteredModelBinder.IsMatch(bindingContext.ModelType))
        {
            &lt;span style="color: blue"&gt;return &lt;/span&gt;filteredModelBinder.BindModel(controllerContext, bindingContext);
        }
    }

    &lt;span style="color: blue"&gt;return base&lt;/span&gt;.BindModel(controllerContext, bindingContext);
}&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;I didn’t address ModelState.&amp;#160; It turns out that this may lead to problems later on when I use the built-in HtmlHelper methods.&amp;#160; These helper methods use ModelState to do things like set error message indicators, fill in values and so on.&amp;#160; But if one of the IFilteredModelBinder implementations binds a value but &lt;em&gt;doesn’t&lt;/em&gt; do the ModelState piece, I start to get some weird behavior in certain scenarios.&lt;/p&gt;

&lt;p&gt;If you start to dig pretty deeply in the DefaultModelBinder code, you’ll find that the base implementation of BindModel will set the ModelState values for certain scenarios.&amp;#160; In our case, we need to allow the IFilteredModelBinders to do something similar.&lt;/p&gt;

&lt;h3&gt;Our slight fix&lt;/h3&gt;

&lt;p&gt;First, we’ll need to fix the IFilteredModelBinder interface to return not just an object, but something that includes the ValueProviderResult object (used by ModelState).&amp;#160; Why not just do object?&amp;#160; Well, we can’t make an assumption on what the underlying model binder implementations need to do with a BindResult, it’s rather custom for each implementation.&amp;#160; So, our IFilteredModelBinder interface becomes:&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public interface &lt;/span&gt;&lt;span style="color: #2b91af"&gt;IFilteredModelBinder
&lt;/span&gt;{
    &lt;span style="color: blue"&gt;bool &lt;/span&gt;IsMatch(&lt;span style="color: #2b91af"&gt;ModelBindingContext &lt;/span&gt;bindingContext);
    &lt;span style="color: #2b91af"&gt;BindResult &lt;/span&gt;BindModel(
        &lt;span style="color: #2b91af"&gt;ControllerContext &lt;/span&gt;controllerContext, 
        &lt;span style="color: #2b91af"&gt;ModelBindingContext &lt;/span&gt;bindingContext);
}&lt;/pre&gt;

&lt;p&gt;That BindResult object is not in MVC, it’s something we have to define:&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;BindResult
&lt;/span&gt;{
    &lt;span style="color: blue"&gt;public object &lt;/span&gt;Value { &lt;span style="color: blue"&gt;get&lt;/span&gt;; &lt;span style="color: blue"&gt;private set&lt;/span&gt;; }
    &lt;span style="color: blue"&gt;public &lt;/span&gt;&lt;span style="color: #2b91af"&gt;ValueProviderResult &lt;/span&gt;ValueProviderResult { &lt;span style="color: blue"&gt;get&lt;/span&gt;; &lt;span style="color: blue"&gt;private set&lt;/span&gt;; }

    &lt;span style="color: blue"&gt;public &lt;/span&gt;BindResult(&lt;span style="color: blue"&gt;object &lt;/span&gt;value, &lt;span style="color: #2b91af"&gt;ValueProviderResult &lt;/span&gt;valueProviderResult)
    {
        Value = value;
        ValueProviderResult = valueProviderResult ?? 
            &lt;span style="color: blue"&gt;new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;ValueProviderResult&lt;/span&gt;(&lt;span style="color: blue"&gt;null&lt;/span&gt;, &lt;span style="color: blue"&gt;string&lt;/span&gt;.Empty, &lt;span style="color: #2b91af"&gt;CultureInfo&lt;/span&gt;.CurrentCulture);
    }
}&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;All object adds is the ValueProviderResult, providing a default null-object implementation if the implementing filtered model binder doesn’t support ValueProviderResults.&amp;#160; With this in place, we need to fix our SmartBinder:&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;SmartBinder &lt;/span&gt;: &lt;span style="color: #2b91af"&gt;DefaultModelBinder
&lt;/span&gt;{
    &lt;span style="color: blue"&gt;private readonly &lt;/span&gt;&lt;span style="color: #2b91af"&gt;IFilteredModelBinder&lt;/span&gt;[] _filteredModelBinders;

    &lt;span style="color: blue"&gt;public &lt;/span&gt;SmartBinder(&lt;span style="color: #2b91af"&gt;IFilteredModelBinder&lt;/span&gt;[] filteredModelBinders)
    {
        _filteredModelBinders = filteredModelBinders;
    }

    &lt;span style="color: blue"&gt;public override object &lt;/span&gt;BindModel(
        &lt;span style="color: #2b91af"&gt;ControllerContext &lt;/span&gt;controllerContext, 
        &lt;span style="color: #2b91af"&gt;ModelBindingContext &lt;/span&gt;bindingContext)
    {
        &lt;span style="color: blue"&gt;foreach &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;filteredModelBinder &lt;span style="color: blue"&gt;in &lt;/span&gt;_filteredModelBinders)
        {
            &lt;span style="color: blue"&gt;if &lt;/span&gt;(filteredModelBinder.IsMatch(bindingContext))
            {
                &lt;span style="color: blue"&gt;var &lt;/span&gt;result = filteredModelBinder.BindModel(controllerContext, bindingContext);

                bindingContext.ModelState.SetModelValue(
                    bindingContext.ModelName, 
                    result.ValueProviderResult);

                &lt;span style="color: blue"&gt;return &lt;/span&gt;result.Value;
            }
        }

        &lt;span style="color: blue"&gt;return base&lt;/span&gt;.BindModel(controllerContext, bindingContext);
    }
}&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;It’s pretty much like the original, except that piece that does the SetModelValue part.&amp;#160; That fills in the necessary pieces for the input HtmlHelper methods to do their thing.&amp;#160; Interestingly, this made another problem go away, around &lt;a href="http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/03/26/mvc-beta-to-rtw-upgrade-issue-addmodelerror-and-nullreferenceexceptions.aspx"&gt;ModelState.AddModelError&lt;/a&gt;.&amp;#160; It turns out I just missed a piece in the custom model binding code.&lt;/p&gt;

&lt;p&gt;So just a note to custom model binder implementors – don’t forget to populate the appropriate ModelState values, especially if the results of the binding operation will be used in conjunction with the HtmlHelper extensions.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;a href="http://www.dotnetkicks.com/kick/?title=A+better+Model+Binder+addendum&amp;url=http%3a%2f%2fwww.lostechies.com%2fblogs%2fjimmy_bogard%2farchive%2f2009%2f11%2f19%2fa-better-model-binder-addendum.aspx"&gt;&lt;img src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fwww.lostechies.com%2fblogs%2fjimmy_bogard%2farchive%2f2009%2f11%2f19%2fa-better-model-binder-addendum.aspx" border="0" alt="Kick It on DotNetKicks.com" /&gt;&lt;/a&gt;&lt;img src="http://www.lostechies.com/aggbug.aspx?PostID=32917" width="1" height="1"&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GrabBagOfT?a=bBS8Y_0VOmw:kVNAUW2umqk:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GrabBagOfT?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GrabBagOfT?a=bBS8Y_0VOmw:kVNAUW2umqk:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GrabBagOfT?i=bBS8Y_0VOmw:kVNAUW2umqk:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GrabBagOfT?a=bBS8Y_0VOmw:kVNAUW2umqk:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GrabBagOfT?i=bBS8Y_0VOmw:kVNAUW2umqk:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GrabBagOfT/~4/bBS8Y_0VOmw" height="1" width="1"/&gt;</description><slash:comments xmlns:slash="http://purl.org/rss/1.0/modules/slash/">2</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://www.lostechies.com/blogs/jimmy_bogard/rsscomments.aspx?PostID=32917</wfw:commentRss><wfw:comment xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://www.lostechies.com/blogs/jimmy_bogard/commentapi.aspx?PostID=32917</wfw:comment><feedburner:origLink>http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/11/19/a-better-model-binder-addendum.aspx</feedburner:origLink></item><item><title>How to annoy your teammates</title><link>http://feedproxy.google.com/~r/GrabBagOfT/~3/kd2E-YLhehI/how-to-annoy-your-teammates.aspx</link><category>Misc</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">bogardj</dc:creator><pubDate>Fri, 13 Nov 2009 06:22:55 PST</pubDate><guid isPermaLink="false">ded273ab-9e87-4979-8222-e4e2e46f1b46:30578</guid><description>&lt;p&gt;In 3 easy steps:&lt;/p&gt;  &lt;h3&gt;Step 1: Perform a change that affects many, many files&lt;/h3&gt;  &lt;p&gt;My favorite is a namespace rename.&amp;#160; Other choices include deleting a core marker interface, renaming a layer supertype class, or changing the folder structure in a project.&lt;/p&gt;  &lt;p&gt;Typically, VisualStudio will crash when I do this, though I’m not sure if it’s VS choking or ReSharper.&amp;#160; Either way, a large automatic refactoring will cause a crash.&lt;/p&gt;  &lt;h3&gt;Step 2: After the VS crash, re-open the solution and choose to recover all unsaved files&lt;/h3&gt;  &lt;p&gt;ReSharper likely made it quite a ways through your refactoring before VS crashed.&amp;#160; All those unsaved files were likely cached for recovery by VisualStudio, so you can probably get a lot of those changes back.&amp;#160; Instead of choosing to revert your local changes, go ahead and recover all those files.&amp;#160; It’s key for maximum annoyance.&lt;/p&gt;  &lt;h3&gt;Step 3: Merge these recovered files back to trunk&lt;/h3&gt;  &lt;p&gt;Since this was an &lt;em&gt;enormous&lt;/em&gt; refactoring, maybe affecting hundreds of files, you’ll want to merge your changes back to trunk as soon as possible.&amp;#160; After all, you don’t want to be the sucker merging after everyone else has finished their work.&lt;/p&gt;  &lt;p&gt;When you’re done, your co-workers will be greeted with one annoying merge, and another fun dialog that never seems to quite go away:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.lostechies.com/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/jimmy_5F00_bogard/image_5F00_74481858.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://www.lostechies.com/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/jimmy_5F00_bogard/image_5F00_thumb_5F00_3AC50861.png" width="451" height="242" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Just when you think you caught the last of the recovered files that had its line endings screwed up, another one pops up.&amp;#160; They’ll hate you once because of the core change that affected so many files, making it quite annoying to merge back to trunk.&amp;#160; But they’ll form that long-lasting, persistent hate because of this dialog box that keeps coming up.&amp;#160; Yes, there is that little checkbox at the bottom, but who reads these things anyway?&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;a href="http://www.dotnetkicks.com/kick/?title=How+to+annoy+your+teammates&amp;url=http%3a%2f%2fwww.lostechies.com%2fblogs%2fjimmy_bogard%2farchive%2f2009%2f11%2f13%2fhow-to-annoy-your-teammates.aspx"&gt;&lt;img src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fwww.lostechies.com%2fblogs%2fjimmy_bogard%2farchive%2f2009%2f11%2f13%2fhow-to-annoy-your-teammates.aspx" border="0" alt="Kick It on DotNetKicks.com" /&gt;&lt;/a&gt;&lt;img src="http://www.lostechies.com/aggbug.aspx?PostID=30578" width="1" height="1"&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GrabBagOfT?a=kd2E-YLhehI:9uC5y7JVfNE:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GrabBagOfT?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GrabBagOfT?a=kd2E-YLhehI:9uC5y7JVfNE:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GrabBagOfT?i=kd2E-YLhehI:9uC5y7JVfNE:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GrabBagOfT?a=kd2E-YLhehI:9uC5y7JVfNE:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GrabBagOfT?i=kd2E-YLhehI:9uC5y7JVfNE:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GrabBagOfT/~4/kd2E-YLhehI" height="1" width="1"/&gt;</description><slash:comments xmlns:slash="http://purl.org/rss/1.0/modules/slash/">9</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://www.lostechies.com/blogs/jimmy_bogard/rsscomments.aspx?PostID=30578</wfw:commentRss><wfw:comment xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://www.lostechies.com/blogs/jimmy_bogard/commentapi.aspx?PostID=30578</wfw:comment><feedburner:origLink>http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/11/13/how-to-annoy-your-teammates.aspx</feedburner:origLink></item><item><title>Willed and forced design</title><link>http://feedproxy.google.com/~r/GrabBagOfT/~3/hPccuMKJZC4/willed-and-forced-design.aspx</link><category>TDD</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">bogardj</dc:creator><pubDate>Thu, 12 Nov 2009 18:32:19 PST</pubDate><guid isPermaLink="false">ded273ab-9e87-4979-8222-e4e2e46f1b46:30528</guid><description>&lt;p&gt;Roy Osherove, as a TypeMock employee, presents quite a dilemma from opinionated TDD blog posts simply because whether he has one or not, there’s always the question of agenda.&amp;#160; Which is quite unfortunate, everyone has some sort of selfish agenda at some level.&amp;#160; One of those posts came up in a commentary on &lt;a href="http://weblogs.asp.net/rosherove/archive/2009/11/12/test-driven-design-willed-vs-forced-designs.aspx"&gt;willed and forced design&lt;/a&gt;, with respect to the usage of mocking frameworks:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;strong&gt;#1 Willed Design&lt;/strong&gt;&lt;/p&gt;    &lt;blockquote&gt;     &lt;p&gt;By writing tests, you can observe the usability of your design from a consumer perspective, and can decide whether or not you like it, and change it accordingly&lt;/p&gt;   &lt;/blockquote&gt;    &lt;p&gt;&lt;strong&gt;#2 Forced Design&lt;/strong&gt;&lt;/p&gt;    &lt;blockquote&gt;     &lt;p&gt;By using a subset of the available isolation frameworks(rhino, moq, nmock) or specific techniques *manual mocks and stubs) you discover cases that are not technically “mockable” or “fakeable” and use that as a sign for design change.&lt;/p&gt;   &lt;/blockquote&gt; &lt;/blockquote&gt;  &lt;p&gt;After about 2 seconds of playing around with dynamic languages such as Ruby and JavaScript, the idea that TypeMock is some how “impure” seemed rather silly.&amp;#160; Yes, it bends the CLR in crazy ways, but in and of itself, I believe it has its place.&lt;/p&gt;  &lt;p&gt;However, I highly disagree with Roy that #2 is bad, that a tool shouldn’t force my hand in a design.&amp;#160; But there’s a bit of a straw man here – it’s not the &lt;em&gt;tool&lt;/em&gt; forcing my design, it’s the &lt;em&gt;test&lt;/em&gt; telling me where I need to change my design.&amp;#160; Quite simply, if I run up against something hard to test, my first choice is to isolate that piece.&amp;#160; Here’s an example:&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public &lt;/span&gt;&lt;span style="color: #2b91af"&gt;ActionResult &lt;/span&gt;Index()
{
    &lt;span style="color: blue"&gt;var &lt;/span&gt;user = Session[&lt;span style="color: #a31515"&gt;&amp;quot;CurrentUser&amp;quot;&lt;/span&gt;];

    &lt;span style="color: blue"&gt;return &lt;/span&gt;View(user);
}&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;This piece is awkward to test, even though HttpSessionBase or whatever is a class with virtual methods, and it makes the test ugly.&amp;#160; Yes, I could mock the crap out of this heavyweight object, but that’s not really helping me out, is it?&amp;#160; Instead, I’ll isolate the ugly:&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;private readonly &lt;/span&gt;&lt;span style="color: #2b91af"&gt;IUserSession &lt;/span&gt;_userSession;

&lt;span style="color: blue"&gt;public &lt;/span&gt;HomeController(&lt;span style="color: #2b91af"&gt;IUserSession &lt;/span&gt;userSession)
{
    _userSession = userSession;
}

&lt;span style="color: blue"&gt;public &lt;/span&gt;&lt;span style="color: #2b91af"&gt;ActionResult &lt;/span&gt;Index()
{
    &lt;span style="color: blue"&gt;var &lt;/span&gt;user = _userSession.GetUser();

    &lt;span style="color: blue"&gt;return &lt;/span&gt;View(user);
}&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;Instead of using a dictionary directly, I’ll hide that business behind a facade service, keeping the dictionary ugliness and duplication in one highly cohesive class.&amp;#160; But it was the _test_ that led me to do this, because I’m tuned in to pieces that are hard to mock.&amp;#160; File I/O?&amp;#160; Facade.&amp;#160; Web service proxies?&amp;#160; Facade.&amp;#160; Registry, HttpContext, etc. etc?&amp;#160; All facade.&amp;#160; I want to isolate my core application from the untestable pieces, not because they’re necessary untestable, but because I don’t want to couple my application directly to these external services.&amp;#160; Putting these behind targeted facades eliminates all that duplication that directly using these APIs tends to encourage.&lt;/p&gt;

&lt;p&gt;There are plenty of APIs that slapping a simple facade over tends to make things worse, not better, such as:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Workflow Foundation&lt;/li&gt;

  &lt;li&gt;SharePoint&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And any other framework that requires you to tiiiiightly integrate with in order to be successful.&amp;#160; So where does that leave me and TypeMock?&amp;#160; Well, I don’t use any of those frameworks that require tight coupling, and I don’t have any pain with my current tool of choice in .NET (Rhino Mocks), so why switch exactly?&amp;#160; For those rare occasions I might need TypeMock (DateTime.Now), I &lt;a href="http://www.lostechies.com/blogs/jimmy_bogard/archive/2008/11/09/systemtime-versus-isystemclock-dependencies-revisited.aspx"&gt;already have a solution&lt;/a&gt; I quite like.&amp;#160; Yes, TypeMock could be free, but the only compelling reason I’d switch would be to another free tool with a better, clearer to write, read and scan API.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;a href="http://www.dotnetkicks.com/kick/?title=Willed+and+forced+design&amp;url=http%3a%2f%2fwww.lostechies.com%2fblogs%2fjimmy_bogard%2farchive%2f2009%2f11%2f12%2fwilled-and-forced-design.aspx"&gt;&lt;img src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fwww.lostechies.com%2fblogs%2fjimmy_bogard%2farchive%2f2009%2f11%2f12%2fwilled-and-forced-design.aspx" border="0" alt="Kick It on DotNetKicks.com" /&gt;&lt;/a&gt;&lt;img src="http://www.lostechies.com/aggbug.aspx?PostID=30528" width="1" height="1"&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GrabBagOfT?a=hPccuMKJZC4:zHVs41W1hF4:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GrabBagOfT?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GrabBagOfT?a=hPccuMKJZC4:zHVs41W1hF4:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GrabBagOfT?i=hPccuMKJZC4:zHVs41W1hF4:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GrabBagOfT?a=hPccuMKJZC4:zHVs41W1hF4:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GrabBagOfT?i=hPccuMKJZC4:zHVs41W1hF4:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GrabBagOfT/~4/hPccuMKJZC4" height="1" width="1"/&gt;</description><slash:comments xmlns:slash="http://purl.org/rss/1.0/modules/slash/">10</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://www.lostechies.com/blogs/jimmy_bogard/rsscomments.aspx?PostID=30528</wfw:commentRss><wfw:comment xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://www.lostechies.com/blogs/jimmy_bogard/commentapi.aspx?PostID=30528</wfw:comment><feedburner:origLink>http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/11/12/willed-and-forced-design.aspx</feedburner:origLink></item><item><title>Fields and virtual members</title><link>http://feedproxy.google.com/~r/GrabBagOfT/~3/xiYwfDocNI0/fields-and-virtual-members.aspx</link><category>C#</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">bogardj</dc:creator><pubDate>Mon, 09 Nov 2009 06:40:25 PST</pubDate><guid isPermaLink="false">ded273ab-9e87-4979-8222-e4e2e46f1b46:30181</guid><description>&lt;p&gt;It seems like the sillier the bug, the more time you’ll spend debugging it, simply because it’s in functionality you just &lt;em&gt;knew&lt;/em&gt; that worked correctly (and had the tests to back it up).&amp;#160; One problem we hit recently was code that used fields to back virtual properties:&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public abstract class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Enumeration &lt;/span&gt;: &lt;span style="color: #2b91af"&gt;IComparable
&lt;/span&gt;{
    &lt;span style="color: blue"&gt;private readonly int &lt;/span&gt;_value;

    &lt;span style="color: blue"&gt;protected &lt;/span&gt;Enumeration() { }

    &lt;span style="color: blue"&gt;protected &lt;/span&gt;Enumeration(&lt;span style="color: blue"&gt;int &lt;/span&gt;value)
    {
        _value = value;
    }

    &lt;span style="color: blue"&gt;public virtual int &lt;/span&gt;Value
    {
        &lt;span style="color: blue"&gt;get &lt;/span&gt;{ &lt;span style="color: blue"&gt;return &lt;/span&gt;_value; }
    }

    &lt;span style="color: blue"&gt;public virtual int &lt;/span&gt;CompareTo(&lt;span style="color: blue"&gt;object &lt;/span&gt;other)
    {
        &lt;span style="color: blue"&gt;return &lt;/span&gt;_value.CompareTo(((&lt;span style="color: #2b91af"&gt;Enumeration&lt;/span&gt;)other).Value);
    }
}&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;This isn’t a problem in and of itself, except in the behavior of that last method.&amp;#160; Notice its logic uses the &lt;em&gt;field&lt;/em&gt; and not the &lt;em&gt;property&lt;/em&gt;.&amp;#160; I can then define a derived type:&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;PercentDiscount &lt;/span&gt;: &lt;span style="color: #2b91af"&gt;Enumeration
&lt;/span&gt;{
    &lt;span style="color: blue"&gt;public override int &lt;/span&gt;Value
    {
        &lt;span style="color: blue"&gt;get &lt;/span&gt;{ &lt;span style="color: blue"&gt;return &lt;/span&gt;5; }
    }
}&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;But now my CompareTo method breaks because it isn’t using the correct value.&amp;#160; It uses the uninitialized field, causing all sorts of bizarre behavior.&amp;#160; When you have logic errors in places like GetHashCode, Equals or CompareTo, I can guarantee your application will break in the most hair-pulling ways imaginable.&lt;/p&gt;

&lt;h3&gt;Let’s fix it&lt;/h3&gt;

&lt;p&gt;So what’s a better option?&amp;#160; We could just “make sure” that we don’t use the private field, and only use the virtual property.&amp;#160; But I hate conventions that rely on my memory to not make a mistake, I want to fall into the &lt;a href="http://blogs.msdn.com/brada/archive/2003/10/02/50420.aspx"&gt;pit of success&lt;/a&gt;.&amp;#160; One way to do so is to use automatic properties:&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public abstract class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;Enumeration &lt;/span&gt;: &lt;span style="color: #2b91af"&gt;IComparable
&lt;/span&gt;{
    &lt;span style="color: blue"&gt;protected &lt;/span&gt;Enumeration() { }

    &lt;span style="color: blue"&gt;protected &lt;/span&gt;Enumeration(&lt;span style="color: blue"&gt;int &lt;/span&gt;value)
    {
        Value = value;
    }

    &lt;span style="color: blue"&gt;public virtual int &lt;/span&gt;Value
    { 
        &lt;span style="color: blue"&gt;get&lt;/span&gt;;
        &lt;span style="color: blue"&gt;private set&lt;/span&gt;;
    }

    &lt;span style="color: blue"&gt;public virtual int &lt;/span&gt;CompareTo(&lt;span style="color: blue"&gt;object &lt;/span&gt;other)
    {
        &lt;span style="color: blue"&gt;return &lt;/span&gt;Value.CompareTo(((&lt;span style="color: #2b91af"&gt;Enumeration&lt;/span&gt;)other).Value);
    }
}&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;Instead of a backing field, we’ll use an automatic property, making it impossible to use a non-virtual backing field.&amp;#160; We still get the same access modifiers (a virtual, overridable getter, a private setter), plus the benefits of automatic properties.&amp;#160; So now I’m curious: &lt;strong&gt;are there any valid reasons to keep using fields for holding state&lt;/strong&gt;?&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;a href="http://www.dotnetkicks.com/kick/?title=Fields+and+virtual+members&amp;url=http%3a%2f%2fwww.lostechies.com%2fblogs%2fjimmy_bogard%2farchive%2f2009%2f11%2f09%2ffields-and-virtual-members.aspx"&gt;&lt;img src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fwww.lostechies.com%2fblogs%2fjimmy_bogard%2farchive%2f2009%2f11%2f09%2ffields-and-virtual-members.aspx" border="0" alt="Kick It on DotNetKicks.com" /&gt;&lt;/a&gt;&lt;img src="http://www.lostechies.com/aggbug.aspx?PostID=30181" width="1" height="1"&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GrabBagOfT?a=xiYwfDocNI0:WggNhI-E1uU:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GrabBagOfT?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GrabBagOfT?a=xiYwfDocNI0:WggNhI-E1uU:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GrabBagOfT?i=xiYwfDocNI0:WggNhI-E1uU:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GrabBagOfT?a=xiYwfDocNI0:WggNhI-E1uU:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GrabBagOfT?i=xiYwfDocNI0:WggNhI-E1uU:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GrabBagOfT/~4/xiYwfDocNI0" height="1" width="1"/&gt;</description><slash:comments xmlns:slash="http://purl.org/rss/1.0/modules/slash/">18</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://www.lostechies.com/blogs/jimmy_bogard/rsscomments.aspx?PostID=30181</wfw:commentRss><wfw:comment xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://www.lostechies.com/blogs/jimmy_bogard/commentapi.aspx?PostID=30181</wfw:comment><feedburner:origLink>http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/11/09/fields-and-virtual-members.aspx</feedburner:origLink></item><item><title>MVC Best Practices</title><link>http://feedproxy.google.com/~r/GrabBagOfT/~3/2TW4o4UVPoM/mvc-best-practices.aspx</link><category>ASP.NET MVC</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">bogardj</dc:creator><pubDate>Wed, 28 Oct 2009 05:45:26 PDT</pubDate><guid isPermaLink="false">ded273ab-9e87-4979-8222-e4e2e46f1b46:28845</guid><description>&lt;p&gt;Simone has a great post (as usual) on &lt;a href="http://codeclimber.net.nz/archive/2009/10/27/12-asp.net-mvc-best-practices.aspx"&gt;12 ASP.NET MVC Best Practices&lt;/a&gt;:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;strong&gt;Controller:&lt;/strong&gt;&lt;/p&gt;    &lt;p&gt;1 - Delete the AccountController&lt;/p&gt;    &lt;p&gt;2 - Isolate controllers from the outside world&lt;/p&gt;    &lt;p&gt;3 - Use an IoC Container&lt;/p&gt;    &lt;p&gt;4 - So NO to “magic strings”&lt;/p&gt;    &lt;p&gt;5 - Build your own personal conventions&lt;/p&gt;    &lt;p&gt;6 - Pay attention to the verbs&lt;/p&gt;    &lt;p&gt;&lt;strong&gt;Model:&lt;/strong&gt;&lt;/p&gt;    &lt;p&gt;7 – Domain Model != ViewModel&lt;/p&gt;    &lt;p&gt;8 – Use ActionFilters for “shared” data&lt;/p&gt;    &lt;p&gt;&lt;strong&gt;View:&lt;/strong&gt;&lt;/p&gt;    &lt;p&gt;9 – NEVER use code-behind&lt;/p&gt;    &lt;p&gt;10 – Write HTML each time you can&lt;/p&gt;    &lt;p&gt;11 – If there is an if, write an HtmlHelper extension&lt;/p&gt;    &lt;p&gt;12 – Choose your ViewEngine carefully&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;This is a fantastic list, and of course I have my own additions/suggestions :)&amp;#160; First, I’m not a fan of action filters for shared data.&amp;#160; If we’re doing strongly-typed views, using action filters for shared data puts us back into the magic string land, where we have to either use inheritance in our ViewModels, or the dictionary part of ViewData.&amp;#160; I don’t like either approach, I’d &lt;a href="http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/06/18/the-filter-viewdata-anti-pattern.aspx"&gt;rather go with something like RenderAction&lt;/a&gt; to truly enforce SRP in my ViewModels.&amp;#160; Not everyone appreciates the elegance of this approach, but I’m sure they’ll agree eventually.&lt;/p&gt;  &lt;p&gt;I also don’t like writing an HtmlHelper extension just because I have logic in the view.&amp;#160; For a half-decent view engine, it shouldn’t be that problematic to have view logic in the view.&amp;#160; HtmlHelper extensions enforce the Helper object anti-pattern – a bunch of procedural logic hanging off one static class.&amp;#160; Instead, we went the route of building intelligent input builders for input elements, and only really using HtmlHelper when we want to eliminate duplication between multiple views.&amp;#160; I see these constructs:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;HtmlHelper&lt;/li&gt;    &lt;li&gt;Partials&lt;/li&gt;    &lt;li&gt;Master Pages&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;All as means of eliminating duplication in our views, but not much more.&amp;#160; If there’s logic in a view, that’s fine by me as long as it’s not duplicated.&amp;#160; It’s not my fault that C# looks downright bizarre mixed in with HTML markup, but that’s what &lt;a href="http://sparkviewengine.com/"&gt;other view engines are for&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;Finally, if there was an award for How Not to Design a Controller, the AccountController would be the runaway champ.&amp;#160; I still can’t imagine why it’s necessary, and the idea of &lt;a href="http://jeffreypalermo.com/blog/mvccontrib-working-on-portable-areas/"&gt;portable areas&lt;/a&gt; would be a better fit for its functionality than the Thing We Always Delete with Extreme Prejudice.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;a href="http://www.dotnetkicks.com/kick/?title=MVC+Best+Practices&amp;url=http%3a%2f%2fwww.lostechies.com%2fblogs%2fjimmy_bogard%2farchive%2f2009%2f10%2f28%2fmvc-best-practices.aspx"&gt;&lt;img src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fwww.lostechies.com%2fblogs%2fjimmy_bogard%2farchive%2f2009%2f10%2f28%2fmvc-best-practices.aspx" border="0" alt="Kick It on DotNetKicks.com" /&gt;&lt;/a&gt;&lt;img src="http://www.lostechies.com/aggbug.aspx?PostID=28845" width="1" height="1"&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GrabBagOfT?a=2TW4o4UVPoM:G1F0GlxOeFw:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GrabBagOfT?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GrabBagOfT?a=2TW4o4UVPoM:G1F0GlxOeFw:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GrabBagOfT?i=2TW4o4UVPoM:G1F0GlxOeFw:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GrabBagOfT?a=2TW4o4UVPoM:G1F0GlxOeFw:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GrabBagOfT?i=2TW4o4UVPoM:G1F0GlxOeFw:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GrabBagOfT/~4/2TW4o4UVPoM" height="1" width="1"/&gt;</description><slash:comments xmlns:slash="http://purl.org/rss/1.0/modules/slash/">13</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://www.lostechies.com/blogs/jimmy_bogard/rsscomments.aspx?PostID=28845</wfw:commentRss><wfw:comment xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://www.lostechies.com/blogs/jimmy_bogard/commentapi.aspx?PostID=28845</wfw:comment><feedburner:origLink>http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/10/28/mvc-best-practices.aspx</feedburner:origLink></item><item><title>MVC Web Testing Strategies – verifying content</title><link>http://feedproxy.google.com/~r/GrabBagOfT/~3/5zjJp2b46W8/mvc-web-testing-strategies-verifying-content.aspx</link><category>ASP.NET MVC</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">bogardj</dc:creator><pubDate>Tue, 27 Oct 2009 18:51:46 PDT</pubDate><guid isPermaLink="false">ded273ab-9e87-4979-8222-e4e2e46f1b46:28642</guid><description>&lt;p&gt;Some of the questions during the C4MVC presentation concerned how I liked to locate data displayed in the rendered HTML for validation purposes.&amp;#160; You have quite a few options for doing so, such as:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Traverse HTML directly through the DOM &lt;/li&gt;    &lt;li&gt;Using existing semantic HTML information &lt;/li&gt;    &lt;li&gt;Adding semantic HTML information &lt;/li&gt;    &lt;li&gt;Wrapping data with specific HTML &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;I’ll dismiss the first option immediately, it’s quite brittle to traverse only the DOM to locate data on a screen.&amp;#160; If I have to have intimate knowledge of the entire HTML document rendered, the test will become far too dependent on the layout on the screen.&amp;#160; If I decide to move information out of a table and into a definition list, my test will likely break.&lt;/p&gt;  &lt;p&gt;From there, I’m left with basically two options – semantic HTML, whether it’s existing or added, or wrapping each data piece with custom HTML.&amp;#160; In the presentation, two questions came up:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;When locating a piece of information in a row in a table, why not use something more meaningful, such as the underlying entity’s ID to tag the data? &lt;/li&gt;    &lt;li&gt;Again in the table, why not use semantic information on the row, instead of adding meaningless HTML? &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Before we look at the different ways to verify content, let’s first look at what we want to verify.&lt;/p&gt;  &lt;h3&gt;The original content&lt;/h3&gt;  &lt;p&gt;In the scenario I highlighted in the screencast, I walk though editing some information, then verifying that data was changed correctly.&amp;#160; I still have tests for controllers, repositories and so on, but I want a high level scenario-based test to make sure the entire pipeline is working as expected.&amp;#160; We try and pull the information out of the HTML content rendered, using some sort of UI testing tool.&amp;#160; Without modifying the content, here’s what we have to work with: &lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: blue"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;div &lt;/span&gt;&lt;span style="color: red"&gt;id&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;main&amp;quot;&amp;gt;
    &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;h2&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;Products&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;h2&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
    &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;table&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
        &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;thead&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
            &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;tr&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
                &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;Details&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
                &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;Name&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
                &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;Manufacturer&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
                &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;Price&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
            &amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;tr&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
        &amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;thead&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
        &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;tbody&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
            &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;tr&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
                &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;a &lt;/span&gt;&lt;span style="color: red"&gt;href&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;/Product/Edit/1&amp;quot;&amp;gt;&lt;/span&gt;Edit&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;a&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
                &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;Insignia® - 26&amp;quot; Class / 720p / 60Hz / LCD HDTV DVD Combo&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
                &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;Insignia&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
                &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;359.99&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
            &amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;tr&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
            &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;tr&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
                &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;a &lt;/span&gt;&lt;span style="color: red"&gt;href&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;/Product/Edit/2&amp;quot;&amp;gt;&lt;/span&gt;Edit&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;a&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
                &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;Insignia® - 19&amp;quot; Class / 720p / 60Hz / LCD HDTV&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
                &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;Insignia&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
                &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;189.99&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
            &amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;tr&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
            &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;tr&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
                &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;a &lt;/span&gt;&lt;span style="color: red"&gt;href&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;/Product/Edit/3&amp;quot;&amp;gt;&lt;/span&gt;Edit&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;a&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
                &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;Dynex® - 15&amp;quot; Class / 720p / 60Hz / LCD HDTV&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
                &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;Dynex&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
                &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;159.99&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
            &amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;tr&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
        &amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;tbody&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
    &amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;table&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
&lt;/span&gt;&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;There’s more rendered than just this HTML, but this is the piece that really matters.&amp;#160; We have some semantic CSS information, the DIV with an ID of “main”, but that’s about it.&amp;#160; In our CSS, we style our tables based on an ID-Element selector, but we could have just have easily used a class name on the table.&amp;#160; I’d like to verify that the price on the first product was changed correctly, but how do I verify this?&amp;#160; How do I make sure that the value in that one cell is the new price?&lt;/p&gt;

&lt;h3&gt;Choosing a strategy&lt;/h3&gt;

&lt;p&gt;I don’t want false positives or false negatives.&amp;#160; I don’t want to look for just any table or any table cell in the markup, I want really the piece just around “359.99”.&amp;#160; A few options to do so include:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Locating the outer DIV, finding the table, the first row, last cell, etc. &lt;/li&gt;

  &lt;li&gt;Apply a special class to the TD just where that price is shown &lt;/li&gt;

  &lt;li&gt;Add markup only around data-bound elements &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When I first started doing UI testing, I assumed that I wanted to be as semantic and pure as possible, and not try to change my markup just for tests.&amp;#160; That idea lasted about an hour, and reality set in that in general, things not designed with testing in mind will be hard to test.&amp;#160; Hard to write tests, hard to maintain tests.&lt;/p&gt;

&lt;p&gt;My next idea was to add information as needed, perhaps a class name to an existing surrounding element that might double for styling.&amp;#160; But I’ve moved completely away from that strategy as well, as &lt;strong&gt;styling and UI testing are two different concerns that should not mix&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Why keep these concerns separate?&amp;#160; Frankly, because both concerns have two completely different reasons to change.&amp;#160; Styling and behavior (such as IDs added for jQuery) are about design and interaction.&amp;#160; UI testing is about verifying site behavior.&amp;#160; Mixing the two concerns means my tests are more likely to break because I’ve changed the design.&amp;#160; But design can change without behavior changing, so I don’t want to couple my tests to the visual design.&amp;#160; Coupling UI tests to the site design is like unit testing using only the reflection API.&lt;/p&gt;

&lt;p&gt;Instead, I’d rather couple my tests to something that better represents the view – and that would be the ViewModel.&lt;/p&gt;

&lt;h3&gt;Strongly-typed views, strongly-typed tests&lt;/h3&gt;

&lt;p&gt;We use strongly-typed views because using a dictionary just leads to brittleness and fear of change.&amp;#160; Strongly-typed views, along with the concept of the &lt;a href="http://codebetter.com/blogs/jeremy.miller/archive/2008/10/23/our-opinions-on-the-asp-net-mvc-introducing-the-thunderdome-principle.aspx"&gt;Thunderdome Principle&lt;/a&gt;, let us move past the issues of broken views and broken model binding.&amp;#160; Instead of writing black-box tests against our UI, we can take advantage of the strongly-typed views, and modify our HTML rendered to get access to the rendered ViewModel.&amp;#160; If you look at views from the point of view of “how to render a ViewModel”, then your UI tests can now talk in the language of the ViewModel.&lt;/p&gt;

&lt;p&gt;But to help our UI tests out, we need to be able to locate information rendered from a ViewModel.&amp;#160; Looking at our original HTML, the most fool-proof, straightforward way to do this would be to add HTML as close to the rendered ViewModel as possible.&amp;#160; Let’s look at our view to see where this might be:&lt;/p&gt;

&lt;pre class="code"&gt;    &lt;span style="color: blue"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;h2&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;Products&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;h2&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
    &lt;/span&gt;&lt;span style="background: #ffee62"&gt;&amp;lt;%&lt;/span&gt; &lt;span style="color: blue"&gt;var &lt;/span&gt;products = Model; &lt;span style="background: #ffee62"&gt;%&amp;gt;
&lt;/span&gt;    
    &lt;span style="color: blue"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;table&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
        &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;thead&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
            &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;tr&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
                &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;Details&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
                &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;Name&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
                &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;Manufacturer&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
                &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;Price&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
            &amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;tr&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
        &amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;thead&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
        &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;tbody&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
        &lt;/span&gt;&lt;span style="background: #ffee62"&gt;&amp;lt;%&lt;/span&gt; &lt;span style="color: blue"&gt;foreach &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;product &lt;span style="color: blue"&gt;in &lt;/span&gt;products) { &lt;span style="background: #ffee62"&gt;%&amp;gt;
&lt;/span&gt;            &lt;span style="color: blue"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;tr&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
                &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;&lt;span style="background: #ffee62"&gt;&amp;lt;%&lt;/span&gt;&lt;span style="color: blue"&gt;= &lt;/span&gt;Html.ActionLink(&lt;span style="color: #a31515"&gt;&amp;quot;Edit&amp;quot;&lt;/span&gt;, &lt;span style="color: #a31515"&gt;&amp;quot;Edit&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;new &lt;/span&gt;{ id = product.Id }) &lt;span style="background: #ffee62"&gt;%&amp;gt;&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
                &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;&lt;span style="background: #ffee62"&gt;&amp;lt;%&lt;/span&gt;&lt;span style="color: blue"&gt;= &lt;/span&gt;product.Name &lt;span style="background: #ffee62"&gt;%&amp;gt;&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
                &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;&lt;span style="background: #ffee62"&gt;&amp;lt;%&lt;/span&gt;&lt;span style="color: blue"&gt;= &lt;/span&gt;product.ManufacturerName &lt;span style="background: #ffee62"&gt;%&amp;gt;&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
                &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;&lt;span style="background: #ffee62"&gt;&amp;lt;%&lt;/span&gt;&lt;span style="color: blue"&gt;= &lt;/span&gt;product.Price &lt;span style="background: #ffee62"&gt;%&amp;gt;&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
            &amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;tr&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
        &lt;/span&gt;&lt;span style="background: #ffee62"&gt;&amp;lt;%&lt;/span&gt; } &lt;span style="background: #ffee62"&gt;%&amp;gt;
&lt;/span&gt;        &lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;tbody&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
    &amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;table&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
&lt;/span&gt;&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;So what’s the ideal spot here?&amp;#160; On the table?&amp;#160; The table body?&amp;#160; Row?&amp;#160; Cell?&amp;#160; These are all possible…and all coupled to the design of the UI, rather than the shape of the ViewModel.&amp;#160; Instead of coupling to the design of the HTML, we can instead change every spot where we render the ViewModel directly to have extra HTML used only for UI tests.&amp;#160; In the presentation, I move towards this model:&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;tbody&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
&lt;/span&gt;&lt;span style="background: #ffee62"&gt;&amp;lt;%&lt;/span&gt; &lt;span style="color: blue"&gt;var &lt;/span&gt;i = 0; &lt;span style="background: #ffee62"&gt;%&amp;gt;
&amp;lt;%&lt;/span&gt; &lt;span style="color: blue"&gt;foreach &lt;/span&gt;(&lt;span style="color: blue"&gt;var &lt;/span&gt;product &lt;span style="color: blue"&gt;in &lt;/span&gt;products) { &lt;span style="background: #ffee62"&gt;%&amp;gt;
&lt;/span&gt;    &lt;span style="color: blue"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;tr&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
        &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;&lt;span style="background: #ffee62"&gt;&amp;lt;%&lt;/span&gt;&lt;span style="color: blue"&gt;= &lt;/span&gt;Html.ActionLink(&lt;span style="color: #a31515"&gt;&amp;quot;Edit&amp;quot;&lt;/span&gt;, &lt;span style="color: #a31515"&gt;&amp;quot;Edit&amp;quot;&lt;/span&gt;, &lt;span style="color: blue"&gt;new &lt;/span&gt;{ id = product.Id }) &lt;span style="background: #ffee62"&gt;%&amp;gt;&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
        &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;&lt;span style="background: #ffee62"&gt;&amp;lt;%&lt;/span&gt;&lt;span style="color: blue"&gt;= &lt;/span&gt;Html.Span(m =&amp;gt; m.Name) &lt;span style="background: #ffee62"&gt;%&amp;gt;&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
        &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;&lt;span style="background: #ffee62"&gt;&amp;lt;%&lt;/span&gt;&lt;span style="color: blue"&gt;= &lt;/span&gt;Html.Span(m =&amp;gt; m.ManufacturerName) &lt;span style="background: #ffee62"&gt;%&amp;gt;&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
        &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;&lt;span style="background: #ffee62"&gt;&amp;lt;%&lt;/span&gt;&lt;span style="color: blue"&gt;= &lt;/span&gt;Html.Span(m =&amp;gt; m.Price) &lt;span style="background: #ffee62"&gt;%&amp;gt;&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
    &amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;tr&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
&lt;/span&gt;&lt;span style="background: #ffee62"&gt;&amp;lt;%
&lt;/span&gt;    i++;
} &lt;span style="background: #ffee62"&gt;%&amp;gt;
&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;tbody&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;In practice, I’ll use a similar concept to the &lt;a href="http://www.lostechies.com/blogs/hex/archive/2009/06/09/opinionated-input-builders-for-asp-net-mvc-using-partials-part-i.aspx"&gt;Opinionated Input Builders Eric highlighted a while back&lt;/a&gt;.&amp;#160; Instead of input elements, I’ll solely render at the core, span tags.&amp;#160; Here is the rendered HTML:&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;div &lt;/span&gt;&lt;span style="color: red"&gt;id&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;main&amp;quot;&amp;gt;
    &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;h2&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;Products&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;h2&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
    &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;table&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
        &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;thead&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
            &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;tr&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
                &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;Details&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
                &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;Name&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
                &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;Manufacturer&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
                &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;Price&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;

            &amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;tr&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
        &amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;thead&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
        &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;tbody&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
            &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;tr&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
                &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;a &lt;/span&gt;&lt;span style="color: red"&gt;href&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;/Product/Edit/1&amp;quot;&amp;gt;&lt;/span&gt;Edit&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;a&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
                &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;span &lt;/span&gt;&lt;span style="color: red"&gt;id&lt;/span&gt;&lt;span style="color: blue"&gt;='_0__Name'&amp;gt;&lt;/span&gt;Insignia® - 26&amp;quot; Class / 720p / 60Hz / LCD HDTV DVD Combo&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;span&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
                &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;span &lt;/span&gt;&lt;span style="color: red"&gt;id&lt;/span&gt;&lt;span style="color: blue"&gt;='_0__ManufacturerName'&amp;gt;&lt;/span&gt;Insignia&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;span&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
                &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;span &lt;/span&gt;&lt;span style="color: red"&gt;id&lt;/span&gt;&lt;span style="color: blue"&gt;='_0__Price'&amp;gt;&lt;/span&gt;359.99&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;span&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
            &amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;tr&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
            &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;tr&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
                &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;a &lt;/span&gt;&lt;span style="color: red"&gt;href&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;/Product/Edit/2&amp;quot;&amp;gt;&lt;/span&gt;Edit&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;a&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
                &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;span &lt;/span&gt;&lt;span style="color: red"&gt;id&lt;/span&gt;&lt;span style="color: blue"&gt;='_1__Name'&amp;gt;&lt;/span&gt;Insignia® - 19&amp;quot; Class / 720p / 60Hz / LCD HDTV&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;span&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
                &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;span &lt;/span&gt;&lt;span style="color: red"&gt;id&lt;/span&gt;&lt;span style="color: blue"&gt;='_1__ManufacturerName'&amp;gt;&lt;/span&gt;Insignia&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;span&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
                &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;span &lt;/span&gt;&lt;span style="color: red"&gt;id&lt;/span&gt;&lt;span style="color: blue"&gt;='_1__Price'&amp;gt;&lt;/span&gt;189.99&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;span&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
            &amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;tr&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
            &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;tr&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
                &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;a &lt;/span&gt;&lt;span style="color: red"&gt;href&lt;/span&gt;&lt;span style="color: blue"&gt;=&amp;quot;/Product/Edit/3&amp;quot;&amp;gt;&lt;/span&gt;Edit&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;a&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
                &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;span &lt;/span&gt;&lt;span style="color: red"&gt;id&lt;/span&gt;&lt;span style="color: blue"&gt;='_2__Name'&amp;gt;&lt;/span&gt;Dynex® - 15&amp;quot; Class / 720p / 60Hz / LCD HDTV&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;span&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
                &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;span &lt;/span&gt;&lt;span style="color: red"&gt;id&lt;/span&gt;&lt;span style="color: blue"&gt;='_2__ManufacturerName'&amp;gt;&lt;/span&gt;Dynex&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;span&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
                &amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;span &lt;/span&gt;&lt;span style="color: red"&gt;id&lt;/span&gt;&lt;span style="color: blue"&gt;='_2__Price'&amp;gt;&lt;/span&gt;159.99&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;span&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;td&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
            &amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;tr&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
        &amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;tbody&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
    &amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;table&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;
&lt;/span&gt;&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;Notice that each data-bound output is wrapped with a span tag – and a special span tag at that.&amp;#160; The span tag has a unique ID that represents the expression used to render that piece of data.&amp;#160; The first product name shown comes from the expression “m =&amp;gt; m[0].Name”.&amp;#160; Because I know the ViewModel used to render this view, I can use an expression combined with a UI testing tool to locate the rendering of any piece of information on the screen.&lt;/p&gt;

&lt;p&gt;The downside of this strategy is the amount of HTML added to the final document render.&amp;#160; But since we’ve separated HTML used for styling and behavior, and HTML rendered strictly for testing, we can put a switch in our application so that for environments not doing UI testing, the span tag isn’t ever rendered!&amp;#160; We can then get the best of both worlds – non-obtrusive JavaScript on top of semantic HTML, and flip a switch to be able to get to any piece of information on the rendered HTML.&lt;/p&gt;

&lt;p&gt;Because our UI tests also use the ViewModel types and expressions to locate the same rendered ViewModels that the views used to render, we no longer run into the issue where our UI tests become disconnected from the views rendered.&amp;#160; If we delete a property on our ViewModel, guess what, the UI test no longer compiles!&lt;/p&gt;

&lt;h3&gt;Wrapping it up&lt;/h3&gt;

&lt;p&gt;Developing a UI testing strategy isn’t the easiest thing in the world.&amp;#160; In our current project, I think we had three or four evolutions before we finally settled on a UI testing strategy we like.&amp;#160; And it’s still not perfect – we’re still looking at ways to make UI testing easy, expressive, and more valuable.&amp;#160; But gone are the days where a styling change broke our tests.&amp;#160; When our UI tests fail now, it’s more along the lines of a new required field, changing business rules and so on.&lt;/p&gt;

&lt;p&gt;I don’t like mixing styling and behavior with locating data-bound UI elements, simply because both have very different reasons for change.&amp;#160; But by taking advantage of our strongly-typed views in our UI tests, we can render the ViewModel in such a way that makes it dirt-simple to locate individual pieces of our ViewModel on the final rendered page.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;a href="http://www.dotnetkicks.com/kick/?title=MVC+Web+Testing+Strategies+%e2%80%93+verifying+content&amp;url=http%3a%2f%2fwww.lostechies.com%2fblogs%2fjimmy_bogard%2farchive%2f2009%2f10%2f27%2fmvc-web-testing-strategies-verifying-content.aspx"&gt;&lt;img src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fwww.lostechies.com%2fblogs%2fjimmy_bogard%2farchive%2f2009%2f10%2f27%2fmvc-web-testing-strategies-verifying-content.aspx" border="0" alt="Kick It on DotNetKicks.com" /&gt;&lt;/a&gt;&lt;img src="http://www.lostechies.com/aggbug.aspx?PostID=28642" width="1" height="1"&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GrabBagOfT?a=5zjJp2b46W8:q1pOP63aXS8:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GrabBagOfT?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GrabBagOfT?a=5zjJp2b46W8:q1pOP63aXS8:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GrabBagOfT?i=5zjJp2b46W8:q1pOP63aXS8:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GrabBagOfT?a=5zjJp2b46W8:q1pOP63aXS8:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GrabBagOfT?i=5zjJp2b46W8:q1pOP63aXS8:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GrabBagOfT/~4/5zjJp2b46W8" height="1" width="1"/&gt;</description><slash:comments xmlns:slash="http://purl.org/rss/1.0/modules/slash/">10</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://www.lostechies.com/blogs/jimmy_bogard/rsscomments.aspx?PostID=28642</wfw:commentRss><wfw:comment xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://www.lostechies.com/blogs/jimmy_bogard/commentapi.aspx?PostID=28642</wfw:comment><feedburner:origLink>http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/10/27/mvc-web-testing-strategies-verifying-content.aspx</feedburner:origLink></item><item><title>C4MVC UI Testing screencast posted</title><link>http://feedproxy.google.com/~r/GrabBagOfT/~3/h89Ok1SXwPc/c4mvc-ui-testing-screencast-posted.aspx</link><category>ASP.NET MVC</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">bogardj</dc:creator><pubDate>Thu, 22 Oct 2009 18:37:41 PDT</pubDate><guid isPermaLink="false">ded273ab-9e87-4979-8222-e4e2e46f1b46:27601</guid><description>&lt;p&gt;Yesterday the &lt;a href="http://www.c4mvc.net/"&gt;C4MVC&lt;/a&gt; guys hosted a screencast with me on UI testing ASP.NET MVC.&amp;#160; It was a lot of fun, but I actually forgot I was being videotaped (webcam’d?), so all those times I turned off my mic to catch a drink of water or cough uncontrollably are caught in glorious 370i.&amp;#160; Anyway, check out the video here:&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:5737277B-5D6D-4f48-ABFC-DD9C333F4C5D:974b11f8-c28d-438c-a18e-4b5d362d0af7" class="wlWriterEditableSmartContent"&gt;&lt;div&gt;&lt;embed src="http://www.viddler.com/player/a4c23795//" width="437" height="370" wmode="transparent" type="application/x-shockwave-flash" allowScriptAccess="always" allowFullScreen="true" name="viddler_a4c23795/"&gt;&lt;/embed&gt;&lt;/div&gt;&lt;/div&gt;  &lt;p&gt;Unfortunately, I didn’t leave enough time for questions at the end, and I had some good ones from the audience.&amp;#160; You can also find the &lt;a href="http://grabbagoftimg.s3.amazonaws.com/uitesting-trunk.zip"&gt;complete code and slides here&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;Thanks again to the C4MVC guys for hosting me!&amp;#160; Since I didn’t have time to answer too many questions, feel free to ask them in the comments, or you can always use the Contact Me link.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;a href="http://www.dotnetkicks.com/kick/?title=C4MVC+UI+Testing+screencast+posted&amp;url=http%3a%2f%2fwww.lostechies.com%2fblogs%2fjimmy_bogard%2farchive%2f2009%2f10%2f22%2fc4mvc-ui-testing-screencast-posted.aspx"&gt;&lt;img src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fwww.lostechies.com%2fblogs%2fjimmy_bogard%2farchive%2f2009%2f10%2f22%2fc4mvc-ui-testing-screencast-posted.aspx" border="0" alt="Kick It on DotNetKicks.com" /&gt;&lt;/a&gt;&lt;img src="http://www.lostechies.com/aggbug.aspx?PostID=27601" width="1" height="1"&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GrabBagOfT?a=h89Ok1SXwPc:BEv0cv5CO_I:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GrabBagOfT?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GrabBagOfT?a=h89Ok1SXwPc:BEv0cv5CO_I:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GrabBagOfT?i=h89Ok1SXwPc:BEv0cv5CO_I:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GrabBagOfT?a=h89Ok1SXwPc:BEv0cv5CO_I:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GrabBagOfT?i=h89Ok1SXwPc:BEv0cv5CO_I:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GrabBagOfT/~4/h89Ok1SXwPc" height="1" width="1"/&gt;</description><slash:comments xmlns:slash="http://purl.org/rss/1.0/modules/slash/">7</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://www.lostechies.com/blogs/jimmy_bogard/rsscomments.aspx?PostID=27601</wfw:commentRss><wfw:comment xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://www.lostechies.com/blogs/jimmy_bogard/commentapi.aspx?PostID=27601</wfw:comment><feedburner:origLink>http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/10/22/c4mvc-ui-testing-screencast-posted.aspx</feedburner:origLink></item><item><title>C4MVC meeting on UI testing</title><link>http://feedproxy.google.com/~r/GrabBagOfT/~3/f_o-pmIlQnU/c4mvc-meeting-on-ui-testing.aspx</link><category>ASP.NET MVC</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">bogardj</dc:creator><pubDate>Wed, 21 Oct 2009 06:38:41 PDT</pubDate><guid isPermaLink="false">ded273ab-9e87-4979-8222-e4e2e46f1b46:27314</guid><description>&lt;p&gt;Today I’m showing giving a talk on UI testing MVC applications for the &lt;a href="http://www.c4mvc.net/"&gt;Community for MVC.Net folks&lt;/a&gt;.&amp;#160; Those that went to my presentations in Arkansas and Houston have seen this talk already, so they’re off the hook on this one.&amp;#160; I’ll be covering ways to create UI tests that don’t break when a butterfly flaps its wings in Japan, and how we can design our MVC applications for UI testability.&lt;/p&gt;  &lt;p&gt;The presentation is done via LiveMeeting, (&lt;a href="http://Meeting.c4mvc.net"&gt;http://Meeting.c4mvc.net&lt;/a&gt;), so make sure you’ve hit that link before the start time, which is 12:00 PM CST today.&amp;#160; For more details, check out the C4MVC site:&lt;/p&gt;  &lt;p&gt;&lt;a title="http://www.c4mvc.net/" href="http://www.c4mvc.net/"&gt;http://www.c4mvc.net/&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;In the WebForms world, it was quite difficult to create maintainable UI tests.&amp;#160; But in ASP.NET MVC, we have far more control over the HTML generated, allowing us to design our UI for testability.&amp;#160; It should be a good discussion, I’m looking forward to seeing everyone there!&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;a href="http://www.dotnetkicks.com/kick/?title=C4MVC+meeting+on+UI+testing&amp;url=http%3a%2f%2fwww.lostechies.com%2fblogs%2fjimmy_bogard%2farchive%2f2009%2f10%2f21%2fc4mvc-meeting-on-ui-testing.aspx"&gt;&lt;img src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fwww.lostechies.com%2fblogs%2fjimmy_bogard%2farchive%2f2009%2f10%2f21%2fc4mvc-meeting-on-ui-testing.aspx" border="0" alt="Kick It on DotNetKicks.com" /&gt;&lt;/a&gt;&lt;img src="http://www.lostechies.com/aggbug.aspx?PostID=27314" width="1" height="1"&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GrabBagOfT?a=f_o-pmIlQnU:YKjZob_mMl4:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GrabBagOfT?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GrabBagOfT?a=f_o-pmIlQnU:YKjZob_mMl4:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GrabBagOfT?i=f_o-pmIlQnU:YKjZob_mMl4:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GrabBagOfT?a=f_o-pmIlQnU:YKjZob_mMl4:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GrabBagOfT?i=f_o-pmIlQnU:YKjZob_mMl4:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GrabBagOfT/~4/f_o-pmIlQnU" height="1" width="1"/&gt;</description><slash:comments xmlns:slash="http://purl.org/rss/1.0/modules/slash/">4</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://www.lostechies.com/blogs/jimmy_bogard/rsscomments.aspx?PostID=27314</wfw:commentRss><wfw:comment xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://www.lostechies.com/blogs/jimmy_bogard/commentapi.aspx?PostID=27314</wfw:comment><feedburner:origLink>http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/10/21/c4mvc-meeting-on-ui-testing.aspx</feedburner:origLink></item><item><title>How not to implement a failing test</title><link>http://feedproxy.google.com/~r/GrabBagOfT/~3/Vq_MxSbNoJc/how-not-to-implement-a-failing-test.aspx</link><category>TDD</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">bogardj</dc:creator><pubDate>Mon, 19 Oct 2009 06:45:30 PDT</pubDate><guid isPermaLink="false">ded273ab-9e87-4979-8222-e4e2e46f1b46:26956</guid><description>&lt;p&gt;One of the first things I change in ReSharper, along with one of my biggest pet peeves is a failing test that fails because of something like this:&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public class &lt;/span&gt;&lt;span style="color: #2b91af"&gt;CombinedStreetAddressResolver 
    &lt;/span&gt;: &lt;span style="color: #2b91af"&gt;NullSafeValueResolver&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;Address&lt;/span&gt;, &lt;span style="color: blue"&gt;string&lt;/span&gt;&amp;gt;
{
    &lt;span style="color: blue"&gt;protected override string &lt;/span&gt;ResolveCore(&lt;span style="color: #2b91af"&gt;Address &lt;/span&gt;model)
    {
        &lt;span style="color: blue"&gt;throw new &lt;/span&gt;&lt;span style="color: #2b91af"&gt;NotImplementedException&lt;/span&gt;();
    }
}&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;In the Red-Green-Refactor progression, the Red of a failing test should come from an &lt;em&gt;assertion failure&lt;/em&gt; not a “my code is stupid” failure.&amp;#160; The Red part is intended to triangulate and calibrate your test, to make sure that your test can fail correctly.&amp;#160; A NotImplementedException won’t cause a meaningful failure, and only serves the purpose of getting your code to compile.&lt;/p&gt;

&lt;p&gt;Throwing exceptions means your assertions never get executed in RGR until you attempt to make a passing test, in which case you still haven’t proven your test to be correct.&amp;#160; If you don’t know that your test is correct, you have two points of failure: your test, and code under test.&lt;/p&gt;

&lt;p&gt;That’s why I’ve set ReSharper to return default values instead of throw exceptions.&amp;#160; I want meaningful failures from a valid test, otherwise I’m better off skipping the Red step altogether.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;a href="http://www.dotnetkicks.com/kick/?title=How+not+to+implement+a+failing+test&amp;url=http%3a%2f%2fwww.lostechies.com%2fblogs%2fjimmy_bogard%2farchive%2f2009%2f10%2f19%2fhow-not-to-implement-a-failing-test.aspx"&gt;&lt;img src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fwww.lostechies.com%2fblogs%2fjimmy_bogard%2farchive%2f2009%2f10%2f19%2fhow-not-to-implement-a-failing-test.aspx" border="0" alt="Kick It on DotNetKicks.com" /&gt;&lt;/a&gt;&lt;img src="http://www.lostechies.com/aggbug.aspx?PostID=26956" width="1" height="1"&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GrabBagOfT?a=Vq_MxSbNoJc:zhA98OqIv7I:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GrabBagOfT?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GrabBagOfT?a=Vq_MxSbNoJc:zhA98OqIv7I:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GrabBagOfT?i=Vq_MxSbNoJc:zhA98OqIv7I:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GrabBagOfT?a=Vq_MxSbNoJc:zhA98OqIv7I:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GrabBagOfT?i=Vq_MxSbNoJc:zhA98OqIv7I:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GrabBagOfT/~4/Vq_MxSbNoJc" height="1" width="1"/&gt;</description><slash:comments xmlns:slash="http://purl.org/rss/1.0/modules/slash/">17</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://www.lostechies.com/blogs/jimmy_bogard/rsscomments.aspx?PostID=26956</wfw:commentRss><wfw:comment xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://www.lostechies.com/blogs/jimmy_bogard/commentapi.aspx?PostID=26956</wfw:comment><feedburner:origLink>http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/10/19/how-not-to-implement-a-failing-test.aspx</feedburner:origLink></item><item><title>More missing LINQ operators</title><link>http://feedproxy.google.com/~r/GrabBagOfT/~3/RQGPoK31Bus/more-missing-linq-operators.aspx</link><category>LINQ</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">bogardj</dc:creator><pubDate>Thu, 15 Oct 2009 20:19:55 PDT</pubDate><guid isPermaLink="false">ded273ab-9e87-4979-8222-e4e2e46f1b46:26581</guid><description>&lt;p&gt;Continuing an &lt;a href="http://www.lostechies.com/blogs/jimmy_bogard/archive/2008/06/07/some-improved-linq-operators.aspx"&gt;old post on missing LINQ operators&lt;/a&gt;, the wonders of extension methods allow us as developers to fill potential holes in LINQ operators.&amp;#160; Whether it’s a Zip method (now included in .NET 4.0), or better methods for IComparer-based operators, I find myself adding more and more helpful LINQ operators, I wish were already in the framework.&lt;/p&gt;  &lt;h3&gt;Alternate&lt;/h3&gt;  &lt;p&gt;Do you ever want to weave two collections together, like shuffling a deck of cards?&amp;#160; Well I know I do!&amp;#160; Suppose we have this collection:&lt;/p&gt;  &lt;p&gt;[1, 3, 5]&lt;/p&gt;  &lt;p&gt;And this collection:&lt;/p&gt;  &lt;p&gt;[2, 4, 6]&lt;/p&gt;  &lt;p&gt;I’d like to create new collection that is the alternating items from the first and second list:&lt;/p&gt;  &lt;p&gt;[1, 2, 3, 4, 5, 6]&lt;/p&gt;  &lt;p&gt;Here’s the code to do it:&lt;/p&gt;  &lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public static &lt;/span&gt;&lt;span style="color: #2b91af"&gt;IEnumerable&lt;/span&gt;&amp;lt;TSource&amp;gt; Alternate&amp;lt;TSource&amp;gt;(&lt;span style="color: blue"&gt;this &lt;/span&gt;&lt;span style="color: #2b91af"&gt;IEnumerable&lt;/span&gt;&amp;lt;TSource&amp;gt; first, &lt;span style="color: #2b91af"&gt;IEnumerable&lt;/span&gt;&amp;lt;TSource&amp;gt; second)
{
    &lt;span style="color: blue"&gt;using &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;IEnumerator&lt;/span&gt;&amp;lt;TSource&amp;gt; e1 = first.GetEnumerator())
    &lt;span style="color: blue"&gt;using &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;IEnumerator&lt;/span&gt;&amp;lt;TSource&amp;gt; e2 = second.GetEnumerator())
        &lt;span style="color: blue"&gt;while &lt;/span&gt;(e1.MoveNext() &amp;amp;&amp;amp; e2.MoveNext())
        {
            &lt;span style="color: blue"&gt;yield return &lt;/span&gt;e1.Current;
            &lt;span style="color: blue"&gt;yield return &lt;/span&gt;e2.Current;
        }
}&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;Very simple, I iterate both enumerables at the same time, yielding the first, then second collection’s current item.&amp;#160; So how is this useful?&amp;#160; How about this action:&lt;/p&gt;

&lt;pre class="code"&gt;[&lt;span style="color: #2b91af"&gt;Test&lt;/span&gt;]
&lt;span style="color: blue"&gt;public void &lt;/span&gt;Word_play()
{
    &lt;span style="color: blue"&gt;var &lt;/span&gt;source = &lt;span style="color: blue"&gt;new&lt;/span&gt;[] {&lt;span style="color: #a31515"&gt;&amp;quot;The&amp;quot;&lt;/span&gt;, &lt;span style="color: #a31515"&gt;&amp;quot;quick&amp;quot;&lt;/span&gt;, &lt;span style="color: #a31515"&gt;&amp;quot;brown&amp;quot;&lt;/span&gt;, &lt;span style="color: #a31515"&gt;&amp;quot;fox&amp;quot;&lt;/span&gt;};

    &lt;span style="color: blue"&gt;var &lt;/span&gt;result = source.Alternate(Spaces()).Aggregate(&lt;span style="color: blue"&gt;string&lt;/span&gt;.Empty, (a, b) =&amp;gt; a + b);

    result.ShouldEqual(&lt;span style="color: #a31515"&gt;&amp;quot;The quick brown fox &amp;quot;&lt;/span&gt;);
}

&lt;span style="color: blue"&gt;private &lt;/span&gt;&lt;span style="color: #2b91af"&gt;IEnumerable&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;string&lt;/span&gt;&amp;gt; Spaces()
{
    &lt;span style="color: blue"&gt;while &lt;/span&gt;(&lt;span style="color: blue"&gt;true&lt;/span&gt;)
        &lt;span style="color: blue"&gt;yield return &lt;/span&gt;&lt;span style="color: #a31515"&gt;&amp;quot; &amp;quot;&lt;/span&gt;;
}&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;I cheated a little bit with an infinite sequence (the Spaces() method), but I found this method useful when I had to split, then reconstruct new sequences of strings.&lt;/p&gt;

&lt;h3&gt;Append&lt;/h3&gt;

&lt;p&gt;I really hate this syntax:&lt;/p&gt;

&lt;pre class="code"&gt;[&lt;span style="color: #2b91af"&gt;Test&lt;/span&gt;]
&lt;span style="color: blue"&gt;public void &lt;/span&gt;Bad_concat_method()
{
    &lt;span style="color: blue"&gt;var &lt;/span&gt;ints = &lt;span style="color: blue"&gt;new&lt;/span&gt;[] {1, 2, 3};

    &lt;span style="color: blue"&gt;var &lt;/span&gt;oneToFour = ints.Concat(&lt;span style="color: #2b91af"&gt;Enumerable&lt;/span&gt;.Repeat(4, 1));

    &lt;span style="color: #2b91af"&gt;CollectionAssert&lt;/span&gt;.AreEqual(&lt;span style="color: blue"&gt;new&lt;/span&gt;[] { 1, 2, 3, 4 }, oneToFour.ToArray());
}&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;I want to just stick an item on the end of an existing collection, but I have to use this arcane Enumerable.Repeat method to do so.&amp;#160; Instead, let’s create an operator that lets us tack an item on to the end of a collection:&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public static &lt;/span&gt;&lt;span style="color: #2b91af"&gt;IEnumerable&lt;/span&gt;&amp;lt;TSource&amp;gt; Append&amp;lt;TSource&amp;gt;(&lt;span style="color: blue"&gt;this &lt;/span&gt;&lt;span style="color: #2b91af"&gt;IEnumerable&lt;/span&gt;&amp;lt;TSource&amp;gt; source, TSource element)
{
    &lt;span style="color: blue"&gt;using &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;IEnumerator&lt;/span&gt;&amp;lt;TSource&amp;gt; e1 = source.GetEnumerator())
        &lt;span style="color: blue"&gt;while &lt;/span&gt;(e1.MoveNext())
            &lt;span style="color: blue"&gt;yield return &lt;/span&gt;e1.Current;

    &lt;span style="color: blue"&gt;yield return &lt;/span&gt;element;
}&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;Now our code becomes much easier to understand:&lt;/p&gt;

&lt;pre class="code"&gt;[&lt;span style="color: #2b91af"&gt;Test&lt;/span&gt;]
&lt;span style="color: blue"&gt;public void &lt;/span&gt;Easier_concat_with_append()
{
    &lt;span style="color: blue"&gt;var &lt;/span&gt;ints = &lt;span style="color: blue"&gt;new&lt;/span&gt;[] {1, 2, 3};

    &lt;span style="color: blue"&gt;var &lt;/span&gt;oneToFour = ints.Append(4);

    &lt;span style="color: #2b91af"&gt;CollectionAssert&lt;/span&gt;.AreEqual(&lt;span style="color: blue"&gt;new&lt;/span&gt;[] { 1, 2, 3, 4 }, oneToFour.ToArray());
}&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;h3&gt;Prepend&lt;/h3&gt;

&lt;p&gt;Append wouldn’t be complete without the converse, Prepend, now would it?&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: blue"&gt;public static &lt;/span&gt;&lt;span style="color: #2b91af"&gt;IEnumerable&lt;/span&gt;&amp;lt;TSource&amp;gt; Prepend&amp;lt;TSource&amp;gt;(&lt;span style="color: blue"&gt;this &lt;/span&gt;&lt;span style="color: #2b91af"&gt;IEnumerable&lt;/span&gt;&amp;lt;TSource&amp;gt; source, TSource element)
{
    &lt;span style="color: blue"&gt;yield return &lt;/span&gt;element;

    &lt;span style="color: blue"&gt;using &lt;/span&gt;(&lt;span style="color: #2b91af"&gt;IEnumerator&lt;/span&gt;&amp;lt;TSource&amp;gt; e1 = source.GetEnumerator())
        &lt;span style="color: blue"&gt;while &lt;/span&gt;(e1.MoveNext())
            &lt;span style="color: blue"&gt;yield return &lt;/span&gt;e1.Current;
}&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;Now putting something on the beginning of a list is easier as well:&lt;/p&gt;

&lt;pre class="code"&gt;[&lt;span style="color: #2b91af"&gt;Test&lt;/span&gt;]
&lt;span style="color: blue"&gt;public void &lt;/span&gt;Easier_concat_with_prepend()
{
    &lt;span style="color: blue"&gt;var &lt;/span&gt;ints = &lt;span style="color: blue"&gt;new&lt;/span&gt;[] {1, 2, 3};

    &lt;span style="color: blue"&gt;var &lt;/span&gt;zeroToThree = ints.Prepend(0);

    &lt;span style="color: #2b91af"&gt;CollectionAssert&lt;/span&gt;.AreEqual(&lt;span style="color: blue"&gt;new&lt;/span&gt;[] { 0, 1, 2, 3 }, zeroToThree.ToArray());
}&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;Much more readable.&amp;#160; I have a few more around replacing the IEqualityComparer&amp;lt;T&amp;gt; overloads, but those are a little bit more esoteric in their examples of crazy set-based logic.&amp;#160; What’s really cool about all these methods is they still allow all sorts of fun chaining, allowing me to create very terse chains of operations on lists, with what would have taken a bazillion cryptic for..each loops.&amp;#160; Cool stuff!&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;a href="http://www.dotnetkicks.com/kick/?title=More+missing+LINQ+operators&amp;url=http%3a%2f%2fwww.lostechies.com%2fblogs%2fjimmy_bogard%2farchive%2f2009%2f10%2f15%2fmore-missing-linq-operators.aspx"&gt;&lt;img src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fwww.lostechies.com%2fblogs%2fjimmy_bogard%2farchive%2f2009%2f10%2f15%2fmore-missing-linq-operators.aspx" border="0" alt="Kick It on DotNetKicks.com" /&gt;&lt;/a&gt;&lt;img src="http://www.lostechies.com/aggbug.aspx?PostID=26581" width="1" height="1"&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GrabBagOfT?a=RQGPoK31Bus:3SJDeaTCgoQ:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GrabBagOfT?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GrabBagOfT?a=RQGPoK31Bus:3SJDeaTCgoQ:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GrabBagOfT?i=RQGPoK31Bus:3SJDeaTCgoQ:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GrabBagOfT?a=RQGPoK31Bus:3SJDeaTCgoQ:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GrabBagOfT?i=RQGPoK31Bus:3SJDeaTCgoQ:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GrabBagOfT/~4/RQGPoK31Bus" height="1" width="1"/&gt;</description><slash:comments xmlns:slash="http://purl.org/rss/1.0/modules/slash/">17</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://www.lostechies.com/blogs/jimmy_bogard/rsscomments.aspx?PostID=26581</wfw:commentRss><wfw:comment xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://www.lostechies.com/blogs/jimmy_bogard/commentapi.aspx?PostID=26581</wfw:comment><feedburner:origLink>http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/10/15/more-missing-linq-operators.aspx</feedburner:origLink></item><item><title>Thanks Arkansas!</title><link>http://feedproxy.google.com/~r/GrabBagOfT/~3/SDBzOyNeSuc/thanks-arkansas.aspx</link><category>Misc</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">bogardj</dc:creator><pubDate>Thu, 15 Oct 2009 19:01:17 PDT</pubDate><guid isPermaLink="false">ded273ab-9e87-4979-8222-e4e2e46f1b46:26576</guid><description>&lt;p&gt;This week I made the drive from Austin to Fort Smith and Springdale, Arkansas to speak at the &lt;a href="http://www.fsdnug.org/"&gt;Fort Smith DNUG&lt;/a&gt; and the &lt;a href="http://www.nwadnug.org/"&gt;Northwest Arkansas DNUG&lt;/a&gt; on UI testing ASP.NET MVC.&amp;#160; Although I showed tools such as WatiN and Gallio, I focused more on design for UI testability, and covered items such as:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Site navigation&lt;/li&gt;    &lt;li&gt;Filling out forms&lt;/li&gt;    &lt;li&gt;Verifying results&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;We stepped through a working, but horribly fragile UI test, and walked through how we can put in a few design techniques to share knowledge between view design and the UI test.&amp;#160; Hopefully, my message of “ban test recorders” gets through to someone, as the UI test recorder is something that threw me off for years on creating maintainable UI tests.&lt;/p&gt;  &lt;p&gt;You can find the slides and code here:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://grabbagoftimg.s3.amazonaws.com/uitesting-trunk.zip"&gt;Testing the last mile in ASP.NET MVC&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Thanks to all the guys in Arkansas for the great conversations, and I hope to see everyone soon!&amp;#160; One parting shot from a trip to &lt;a href="http://www.arkansasstateparks.com/devilsden/"&gt;Devil’s Den&lt;/a&gt;:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.lostechies.com/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/jimmy_5F00_bogard/DSC00219_5F00_0B8F93E7.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="DSC00219" border="0" alt="DSC00219" src="http://www.lostechies.com/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/jimmy_5F00_bogard/DSC00219_5F00_thumb_5F00_259379D6.jpg" width="644" height="484" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;My jeans were soaked up to my knees from that hike…&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;a href="http://www.dotnetkicks.com/kick/?title=Thanks+Arkansas!&amp;url=http%3a%2f%2fwww.lostechies.com%2fblogs%2fjimmy_bogard%2farchive%2f2009%2f10%2f15%2fthanks-arkansas.aspx"&gt;&lt;img src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fwww.lostechies.com%2fblogs%2fjimmy_bogard%2farchive%2f2009%2f10%2f15%2fthanks-arkansas.aspx" border="0" alt="Kick It on DotNetKicks.com" /&gt;&lt;/a&gt;&lt;img src="http://www.lostechies.com/aggbug.aspx?PostID=26576" width="1" height="1"&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GrabBagOfT?a=SDBzOyNeSuc:rIllGfk1tLU:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GrabBagOfT?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GrabBagOfT?a=SDBzOyNeSuc:rIllGfk1tLU:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GrabBagOfT?i=SDBzOyNeSuc:rIllGfk1tLU:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GrabBagOfT?a=SDBzOyNeSuc:rIllGfk1tLU:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GrabBagOfT?i=SDBzOyNeSuc:rIllGfk1tLU:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GrabBagOfT/~4/SDBzOyNeSuc" height="1" width="1"/&gt;</description><slash:comments xmlns:slash="http://purl.org/rss/1.0/modules/slash/">4</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://www.lostechies.com/blogs/jimmy_bogard/rsscomments.aspx?PostID=26576</wfw:commentRss><wfw:comment xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://www.lostechies.com/blogs/jimmy_bogard/commentapi.aspx?PostID=26576</wfw:comment><feedburner:origLink>http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/10/15/thanks-arkansas.aspx</feedburner:origLink></item><item><title>An AutoMapper success story</title><link>http://feedproxy.google.com/~r/GrabBagOfT/~3/ezrb2SlOQsY/an-automapper-success-story.aspx</link><category>AutoMapper</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">bogardj</dc:creator><pubDate>Thu, 08 Oct 2009 09:38:32 PDT</pubDate><guid isPermaLink="false">ded273ab-9e87-4979-8222-e4e2e46f1b46:26027</guid><description>&lt;p&gt;I got a cool message on the &lt;a href="http://groups.google.com/group/automapper-users"&gt;AutoMapper mailing list&lt;/a&gt; from &lt;a href="http://consultingblogs.emc.com/howardvanrooijen/"&gt;Howard Van Rooijen&lt;/a&gt; on how they used &lt;a href="http://automapper.codeplex.com/"&gt;AutoMapper&lt;/a&gt; in a site they recently launched to production:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;Hello AutoMapper Community,&lt;/p&gt;    &lt;p&gt;I just wanted to let you know that an e-commerce site which uses AutoMapper as part of it's core architecture has just been released into the wild: &lt;/p&gt;    &lt;p&gt;&lt;a href="http://www.fancydressoutfitters.co.uk/"&gt;http://www.&lt;/a&gt;&lt;a href="http://www.fancydressoutfitters.co.uk/"&gt;fancydressout&lt;/a&gt;&lt;a href="http://www.fancydressoutfitters.co.uk/"&gt;fitters.co.uk&lt;/a&gt;&lt;/p&gt;    &lt;p&gt;And I wanted to say a HUGE thank-you to Jimmy &amp;amp; the Community for this wonderful tool - that helps remove so much commodity plumbing code from the solution. &lt;/p&gt;    &lt;p&gt;We were a little sceptical at the start of the project that AutoMapper would &amp;quot;cut the mustard&amp;quot; when it came to the performance requirements of a public facing, high load, e-commerce site because of the amount of reflection AutoMapper uses at its core, but we have been incredibly impressed with the performance of the solution under load.&lt;/p&gt;    &lt;p&gt;The site is based on the S#arp Architecture Framework and its become very apparent how well AutoMapper fits into MVC style architecture as it enables easy separation of concerns with regards to object conversion (entities &amp;amp; ViewModels). Once we moved from hand-cranked converters to AutoMapper it was amazing how much cleaner our code became - so much so that we modified the overall solution architecture to incorporate explicit mapping layers (see attached image). Our general pattern of usage within MVC is as follows:&lt;/p&gt;    &lt;p&gt;1. Map input into Domain Entities in the Controller&lt;/p&gt;    &lt;p&gt;2. Pass Domain Entities into Task Layer to &amp;quot;do stuff&amp;quot;&lt;/p&gt;    &lt;p&gt;3. Map output of Task Layer (Domain Entities) into ViewModel&lt;/p&gt;    &lt;p&gt;4. Pass ViewModel to ViewEngine&lt;/p&gt;    &lt;p&gt;Simple, slick and clean.&lt;/p&gt;    &lt;p&gt;To formalise the Mapping Layer and make it testable we implemented a simple interface:&lt;/p&gt;    &lt;p&gt;public interface IMapper&amp;lt;TInput, TOutput&amp;gt;&lt;/p&gt;    &lt;p&gt;{&lt;/p&gt;    &lt;p&gt;TOutput MapFrom(TInput input);&lt;/p&gt;    &lt;p&gt;}&lt;/p&gt;    &lt;p&gt;Next we'd implement a custom marker interface so that we could resolve the mapper from the DI container and we adopted the naming convention &amp;lt;Input Type&amp;gt;&amp;lt;Output Type&amp;gt;Mapper:&lt;/p&gt;    &lt;p&gt;public interface IEditModelEntityMapper : IMapper&amp;lt;EditModel, Entity&amp;gt;&lt;/p&gt;    &lt;p&gt;{&lt;/p&gt;    &lt;p&gt;}&lt;/p&gt;    &lt;p&gt;Then finally implement the interface:&lt;/p&gt;    &lt;p&gt;public class EditModelEntityMapper : IEditModelEntityMapper &lt;/p&gt;    &lt;p&gt;{&lt;/p&gt;    &lt;p&gt;public EditModelEntityMapper()&lt;/p&gt;    &lt;p&gt;{&lt;/p&gt;    &lt;p&gt;Mapper.CreateMap&amp;lt;EditModel, Entity&amp;gt;()&lt;/p&gt;    &lt;p&gt;.ForMember(x =&amp;gt; x.Property, y =&amp;gt; y.MapFrom(z =&amp;gt; z.Property));&lt;/p&gt;    &lt;p&gt;}&lt;/p&gt;    &lt;p&gt;public Entity MapFrom(EditModel input)&lt;/p&gt;    &lt;p&gt;{&lt;/p&gt;    &lt;p&gt;return Mapper.Map&amp;lt;EditModel, Entity&amp;gt;(input);&lt;/p&gt;    &lt;p&gt;}&lt;/p&gt;    &lt;p&gt;}&lt;/p&gt;    &lt;p&gt;Then to actually use it in the MVC app:&lt;/p&gt;    &lt;p&gt;public class CustomController : Controller&lt;/p&gt;    &lt;p&gt;{&lt;/p&gt;    &lt;p&gt;private readonly ITasks tasks;&lt;/p&gt;    &lt;p&gt;private readonly IEditModelEntityMapper editModelEntityMapper;&lt;/p&gt;    &lt;p&gt;private readonly IOutputViewModelMapper outputViewModelMapper;&lt;/p&gt;    &lt;p&gt;public CustomController(ITasks tasks, &lt;/p&gt;    &lt;p&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; IEditModelEntityMapper editModelEntityMapper,&lt;/p&gt;    &lt;p&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; IOutputViewModelMapper outputViewModelMapper)&lt;/p&gt;    &lt;p&gt;{&lt;/p&gt;    &lt;p&gt;this.tasks = tasks;&lt;/p&gt;    &lt;p&gt;this.editModelEntityMapper = editModelEntityMapper;&lt;/p&gt;    &lt;p&gt;this.outputViewModelMapper = outputViewModelMapper;&lt;/p&gt;    &lt;p&gt;}&lt;/p&gt;    &lt;p&gt;public ActionResult Index(EditEntity input)&lt;/p&gt;    &lt;p&gt;{&lt;/p&gt;    &lt;p&gt;var entity = this.editModelEntityMapper.MapFrom(input);&lt;/p&gt;    &lt;p&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; var output = this.tasks.DoSomething(entity)&lt;/p&gt;    &lt;p&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; var viewModel = this.outputViewModelMapper.MapFrom(output);&lt;/p&gt;    &lt;p&gt;return View(viewModel);&lt;/p&gt;    &lt;p&gt;}&lt;/p&gt;    &lt;p&gt;}&lt;/p&gt;    &lt;p&gt;For those who are interested - here's a little more info:&lt;/p&gt;    &lt;p&gt;- We ran the project using Scrum and delivered in 20 weeks: 10 x 2 week iterations&lt;/p&gt;    &lt;p&gt;- It's based on the &lt;a href="http://www.sharparchitecture.net/"&gt;S#arp Architecture&lt;/a&gt; framework, which we extended to support, AutoMapper, Spark and ViewModels      &lt;br /&gt;- Solution performs very well: 1000 concurrent users per web server, generating around 180 pages per second across 2x single quad core 64bit servers.&lt;/p&gt;    &lt;p&gt;Again, many thanks,&lt;/p&gt;    &lt;p&gt;Howard&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;Howard also shared a neat little diagram of his architecture:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.lostechies.com/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/jimmy_5F00_bogard/image_5F00_18A008AF.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://www.lostechies.com/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/jimmy_5F00_bogard/image_5F00_thumb_5F00_312FA5FF.png" width="478" height="484" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;This is one of the greatest feelings from doing OSS – that something you created basically just for yourself can also help out other folks out there trying to deliver value for their customers.&amp;#160; Thanks for all the feedback everyone, as well as kudos to the S#arp Architecture team for building such a great framework for MVC.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;a href="http://www.dotnetkicks.com/kick/?title=An+AutoMapper+success+story&amp;url=http%3a%2f%2fwww.lostechies.com%2fblogs%2fjimmy_bogard%2farchive%2f2009%2f10%2f08%2fan-automapper-success-story.aspx"&gt;&lt;img src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fwww.lostechies.com%2fblogs%2fjimmy_bogard%2farchive%2f2009%2f10%2f08%2fan-automapper-success-story.aspx" border="0" alt="Kick It on DotNetKicks.com" /&gt;&lt;/a&gt;&lt;img src="http://www.lostechies.com/aggbug.aspx?PostID=26027" width="1" height="1"&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GrabBagOfT?a=ezrb2SlOQsY:AL1d2cmq9ls:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GrabBagOfT?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GrabBagOfT?a=ezrb2SlOQsY:AL1d2cmq9ls:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GrabBagOfT?i=ezrb2SlOQsY:AL1d2cmq9ls:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GrabBagOfT?a=ezrb2SlOQsY:AL1d2cmq9ls:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GrabBagOfT?i=ezrb2SlOQsY:AL1d2cmq9ls:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GrabBagOfT/~4/ezrb2SlOQsY" height="1" width="1"/&gt;</description><slash:comments xmlns:slash="http://purl.org/rss/1.0/modules/slash/">10</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://www.lostechies.com/blogs/jimmy_bogard/rsscomments.aspx?PostID=26027</wfw:commentRss><wfw:comment xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://www.lostechies.com/blogs/jimmy_bogard/commentapi.aspx?PostID=26027</wfw:comment><feedburner:origLink>http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/10/08/an-automapper-success-story.aspx</feedburner:origLink></item><item><title>Bugs, defects and feedback</title><link>http://feedproxy.google.com/~r/GrabBagOfT/~3/LHOD1n0a_ao/bugs-defects-and-feedback.aspx</link><category>Agile</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">bogardj</dc:creator><pubDate>Wed, 30 Sep 2009 10:58:35 PDT</pubDate><guid isPermaLink="false">ded273ab-9e87-4979-8222-e4e2e46f1b46:25679</guid><description>&lt;p&gt;In &lt;a href="http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/09/29/my-favorite-bug-tracking-system.aspx"&gt;my last post&lt;/a&gt;, I talked about how we like to track bugs, with just pieces of paper.&amp;#160; At lot of the responses were interesting, but I think some came back to the issue of “what is a bug?”&amp;#160; Some felt that bugs needed to live in a more durable system (as if paper isn’t durable, and previous to around 1985 no one knew how to manage information).&amp;#160; But for us, bugs don’t live more than 1 business day, so there’s no need to keep them around, unless we want to do trend analysis.&lt;/p&gt;  &lt;p&gt;But one important issue is how we deal with feedback.&amp;#160; A user can come up to us and say, “the site is broken”.&amp;#160; That could mean a lot of things.&amp;#160; It could mean the site is blowing up, or it could mean that they couldn’t find the “Submit Order” button.&amp;#160; To the user, the site is broken.&lt;/p&gt;  &lt;p&gt;Instead of referring to negative user feedback as “bugs”, we treat them as “issues”.&amp;#160; So what is an “issue”?&amp;#160; &lt;strong&gt;An issue is negative user feedback with potentially actionable fix.&lt;/strong&gt;&amp;#160;&amp;#160; Note “potentially”.&lt;/p&gt;  &lt;h3&gt;The feedback process&lt;/h3&gt;  &lt;p&gt;When we get an issue from a user, the issue goes into triage with the Product Owner.&amp;#160; The Product Owner is the person/team deciding what gets developed, so it’s logical that they decide what action to take with an issue.&amp;#160; An issue falls into two broad categories:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Bug&lt;/li&gt;    &lt;li&gt;Defect&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;&lt;strong&gt;A bug is an issue where the site is unusable or acceptance criteria has not been met&lt;/strong&gt;.&amp;#160; This includes things from “the site is down” to “you misspelled a word”.&amp;#160; All of our stories have tangible and intangible acceptance criteria, functional and non-functional requirements.&amp;#160; The expectations of these have been laid forth a long time ago, so we don’t really negotiate too much on what is or is not a bug.&amp;#160; The user may have a different idea of the classification of bug, but they don’t get to decide.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;A defect is an issue where the site is usable and acceptance criteria have been met, but the application does not work as expected&lt;/strong&gt;.&amp;#160; Things in this category are “data field should be over here”, “add this label”, “this business rule isn’t quite right” and so on.&amp;#160; We don’t expect the users to know all of the stories we’ve developed, but there are still situations where the site isn’t working like it should, but works as agreed.&amp;#160; We work hard to do good JIT analysis, but we still miss things.&lt;/p&gt;  &lt;h3&gt;Actions on bugs&lt;/h3&gt;  &lt;p&gt;Bugs are fixed immediately, with an SLA of one business day.&amp;#160; We do things like branch and release-per-feature, automated builds and deployments, so we’re never more than about 15 minutes away from a commit to production fix.&amp;#160; We refuse to keep an inventory of open bugs, so bugs are fixed as they arrive, and the team will stop their work to fix a bug.&amp;#160; Because we stop-the-line, we strive to eliminate as many bugs as we can.&amp;#160; This includes good design, a strong testing culture, and post-mortems on bugs to see how we can improve the next time around and eliminate the possibility of a bug like that to arise again.&lt;/p&gt;  &lt;h3&gt;Actions on defects&lt;/h3&gt;  &lt;p&gt;Defects need to be analyzed just as much as regular stories need to, so defects enter a second queue where they either turn into stories, or something we call “tweak stories”.&amp;#160; Stories take 1-5 business days to deliver, while a “tweak story” gets turned around as quickly as a bug.&amp;#160; It all comes down to the complexity of the defect.&amp;#160; In any case, the Product Owner prioritizes these defects along with stories, and ultimately it’s the PO that decides when (or if) the team takes action on a defect.&lt;/p&gt;  &lt;h3&gt;The feedback cycle&lt;/h3&gt;  &lt;p&gt;For critical bugs, like “the site blows up”, there is no need to triage.&amp;#160; It’s unacceptable that the site blows up, so we’ll fix those immediately.&amp;#160; But for more subtle feedback, the developer team is ill-equipped to make a determination on the nature of an issue.&amp;#160; Often, the development team attempts to fix every issue that comes their way, whether it’s a bug or not, in an effort to make the user happy.&amp;#160; But the guy that says “the customer is always right”?&amp;#160; He goes broke.&amp;#160; The customer &lt;em&gt;isn’t&lt;/em&gt; always right, so it’s the PO’s responsibility for determining the classification of the issue.&lt;/p&gt;  &lt;p&gt;Ultimately, it’s about roles and responsibilities.&amp;#160; Developer teams are usually quite poor at determining the priority of work, as they don’t always have the deep business/project understanding of scope, deadlines, priorities and so on.&amp;#160; Instead, we place the responsibility of triage and prioritization where it belongs, with the Product Owner.&lt;/p&gt;  &lt;p&gt;With this system in place, we have no need to keep what we call “bugs” around in a durable format.&amp;#160; Once artifacts become durable, people hesitate to delete them, and now they need to become managed.&amp;#160; At one point, I kept bugs.&amp;#160; But I found I never looked at them, as our practice of post-mortems on bugs as they are fixed was perfectly adequate.&amp;#160; Instead, I (or nowadays, the originator) just toss them when they’re done, and it feels quite liberating.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;a href="http://www.dotnetkicks.com/kick/?title=Bugs%2c+defects+and+feedback&amp;url=http%3a%2f%2fwww.lostechies.com%2fblogs%2fjimmy_bogard%2farchive%2f2009%2f09%2f30%2fbugs-defects-and-feedback.aspx"&gt;&lt;img src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fwww.lostechies.com%2fblogs%2fjimmy_bogard%2farchive%2f2009%2f09%2f30%2fbugs-defects-and-feedback.aspx" border="0" alt="Kick It on DotNetKicks.com" /&gt;&lt;/a&gt;&lt;img src="http://www.lostechies.com/aggbug.aspx?PostID=25679" width="1" height="1"&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GrabBagOfT?a=LHOD1n0a_ao:nLBVo2UMis0:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GrabBagOfT?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GrabBagOfT?a=LHOD1n0a_ao:nLBVo2UMis0:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GrabBagOfT?i=LHOD1n0a_ao:nLBVo2UMis0:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GrabBagOfT?a=LHOD1n0a_ao:nLBVo2UMis0:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GrabBagOfT?i=LHOD1n0a_ao:nLBVo2UMis0:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GrabBagOfT/~4/LHOD1n0a_ao" height="1" width="1"/&gt;</description><slash:comments xmlns:slash="http://purl.org/rss/1.0/modules/slash/">5</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://www.lostechies.com/blogs/jimmy_bogard/rsscomments.aspx?PostID=25679</wfw:commentRss><wfw:comment xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://www.lostechies.com/blogs/jimmy_bogard/commentapi.aspx?PostID=25679</wfw:comment><feedburner:origLink>http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/09/30/bugs-defects-and-feedback.aspx</feedburner:origLink></item><item><title>My favorite bug-tracking system</title><link>http://feedproxy.google.com/~r/GrabBagOfT/~3/ir5_vTM7VIM/my-favorite-bug-tracking-system.aspx</link><category>Agile</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">bogardj</dc:creator><pubDate>Tue, 29 Sep 2009 10:57:03 PDT</pubDate><guid isPermaLink="false">ded273ab-9e87-4979-8222-e4e2e46f1b46:25628</guid><description>&lt;p&gt;I’m of the opinion that a process must demonstrate the need for software, before software is put in place to manage that process.&amp;#160; Bug tracking is a process, but often we jump straight to a software solution for managing/tracking bugs before considering lower-tech, lower-risk and lower-overhead approaches.&amp;#160; In our bug-tracking process, we employ a few main principles:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Bugs are a normal part of the development process&lt;/li&gt;    &lt;li&gt;Most bugs are rework, therefore waste, and we should strive to eliminate this waste&lt;/li&gt;    &lt;li&gt;Bugs are fixed as they are found (&lt;a href="http://www.informit.com/articles/article.aspx?p=664147&amp;amp;seqNum=5"&gt;stop-the-line&lt;/a&gt;), and do not live for more than 1 business day&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Given that bugs do not live for more than one business day, it is not necessary for us to maintain an inventory of bugs.&amp;#160; Instead, we want a system than can accurately report a bug, and make it easy for it to be verified.&lt;/p&gt;  &lt;p&gt;With these constraints in mind, my favorite bug tracking system is a &lt;a href="http://www.officemax.com/office-supplies/post-it-notes-flags/post-it-super-sticky-notes/product-ARS23443"&gt;5x8” orange sticky note&lt;/a&gt;:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.lostechies.com/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/jimmy_5F00_bogard/23443p_5F00_011_5F00_1C3BC831.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="23443p_01[1]" border="0" alt="23443p_01[1]" src="http://www.lostechies.com/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/jimmy_5F00_bogard/23443p_5F00_011_5F00_thumb_5F00_1FD9E30E.jpg" width="401" height="357" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;On this giant sticky note, the person that finds the bug writes:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Their initials&lt;/li&gt;    &lt;li&gt;The name of the story&lt;/li&gt;    &lt;li&gt;A short description&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;If there is a screenshot, this will be stapled to the back.&amp;#160; When the developer is done with the bug, they write:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Their initials&lt;/li&gt;    &lt;li&gt;The revision # of the commit that fixed the bug&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;The revision number is used to make sure that the currently deployed version is up-to-date enough to check the fix.&amp;#160; So how does this bug tracking system work?&amp;#160; Here is our process:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Person finds bug.&amp;#160; Person could be anyone, developer, BA, product owner.&lt;/li&gt;    &lt;li&gt;Finder creates orange card for bug, following the specified template (every piece of information goes in a certain spot on the giant post-it)&lt;/li&gt;    &lt;li&gt;If the bug is a blow-up, or blocks the delivery of a story, it is placed on the desk of the tech lead (me)&lt;/li&gt;    &lt;li&gt;If the bug is not specifically called out in acceptance criteria, it is placed on the desk of the BA lead.&lt;/li&gt;    &lt;ul&gt;     &lt;li&gt;These bugs go through triage, because sometimes things “work as designed” but not “work as expected”.&amp;#160; Those in the latter category need to go through the normal story process.&lt;/li&gt;   &lt;/ul&gt;    &lt;li&gt;The tech lead assigns the bug to a developer (or themselves), by placing the bug on the assigned developer’s desk&lt;/li&gt;    &lt;li&gt;When the fix is done, the developer writes their initials and the revision number, and gives the orange card back to the originator, with a smile&lt;/li&gt;    &lt;li&gt;The originator of the bug verifies the fix, and places the orange card in the trash with a “thumbs up” to let the developer know the bug is verified fixed.&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;This is about as lightweight as we can get it, without chaos.&amp;#160; This process has evolved over time, but we’ve always fit our tools (post-its) to our process, instead of fitting our process to a tool.&amp;#160; We’re all in the same room, so there is absolutely zero need to track bugs electronically.&amp;#160; Tracking them electronically would create waste, as we would now have inventory to manage in some other system.&amp;#160; It really can’t get much easier than a bright piece of paper on my desk.&lt;/p&gt;  &lt;h3&gt;Fit the tool to the process&lt;/h3&gt;  &lt;p&gt;This process evolved given the attributes of the constraints of our system.&amp;#160; We all sit in the same room.&amp;#160; We strive for face-to-face, verbal communication over all other forms.&amp;#160; We don’t want to add overhead by tracking information that no one cares about.&amp;#160; When someone suggests to use a software bug tracker, it needs to come from a need to have that information in that format, not because geeks like software.&lt;/p&gt;  &lt;p&gt;But what if we had different constraints?&amp;#160; What if we weren’t all in the same room?&amp;#160; Fine, software it is!&amp;#160; Until then, I’ll choose the absolute lowest overhead solution.&lt;/p&gt;  &lt;p&gt;But how does it scale?&amp;#160; Well, if you keep an inventory of bugs for more than a day, not too well.&amp;#160; If bugs are fixed when they happen, it’s just not possible to have more than a dozen bugs out at a time.&amp;#160; Scaling needs a vector, you have to understand in which direction it can scale.&amp;#160; It scales with the size of the team, but not with geography or volume.&amp;#160; That’s fine, we keep those two small and constant.&amp;#160; Those choices were intentional, as separated teams and higher volume through poor quality or turnaround decreases delivery throughput.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;a href="http://www.dotnetkicks.com/kick/?title=My+favorite+bug-tracking+system&amp;url=http%3a%2f%2fwww.lostechies.com%2fblogs%2fjimmy_bogard%2farchive%2f2009%2f09%2f29%2fmy-favorite-bug-tracking-system.aspx"&gt;&lt;img src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fwww.lostechies.com%2fblogs%2fjimmy_bogard%2farchive%2f2009%2f09%2f29%2fmy-favorite-bug-tracking-system.aspx" border="0" alt="Kick It on DotNetKicks.com" /&gt;&lt;/a&gt;&lt;img src="http://www.lostechies.com/aggbug.aspx?PostID=25628" width="1" height="1"&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GrabBagOfT?a=ir5_vTM7VIM:LacGxu2uDhk:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GrabBagOfT?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GrabBagOfT?a=ir5_vTM7VIM:LacGxu2uDhk:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GrabBagOfT?i=ir5_vTM7VIM:LacGxu2uDhk:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GrabBagOfT?a=ir5_vTM7VIM:LacGxu2uDhk:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GrabBagOfT?i=ir5_vTM7VIM:LacGxu2uDhk:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GrabBagOfT/~4/ir5_vTM7VIM" height="1" width="1"/&gt;</description><slash:comments xmlns:slash="http://purl.org/rss/1.0/modules/slash/">19</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://www.lostechies.com/blogs/jimmy_bogard/rsscomments.aspx?PostID=25628</wfw:commentRss><wfw:comment xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://www.lostechies.com/blogs/jimmy_bogard/commentapi.aspx?PostID=25628</wfw:comment><feedburner:origLink>http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/09/29/my-favorite-bug-tracking-system.aspx</feedburner:origLink></item><item><title>Thanks Houston TechFest!</title><link>http://feedproxy.google.com/~r/GrabBagOfT/~3/UrhJgM5djrQ/thanks-houston-techfest.aspx</link><category>ASP.NET MVC</category><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">bogardj</dc:creator><pubDate>Mon, 28 Sep 2009 05:41:01 PDT</pubDate><guid isPermaLink="false">ded273ab-9e87-4979-8222-e4e2e46f1b46:25606</guid><description>&lt;p&gt;This past weekend I gave a talk on one of my favorite topics – UI testing.&amp;#160; In it, I focused almost exclusively on techniques for authoring maintainable UI tests, and how design for testability extends to views, models and controllers in MVC land.&amp;#160; It’s trivial to write UI tests that break easily, but not much more difficult to write maintainable tests, given that you’ve intentionally designed your UI layer for UI testability.&lt;/p&gt;  &lt;p&gt;Big thanks to the Houston TechFest organizers, who put on a fantastic conference, and to all the attendees, who had to put up with me for an hour.&amp;#160; For those that are interested, you can find the code and slides here:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://grabbagoftimg.s3.amazonaws.com/uitesting-trunk.zip"&gt;Testing the Last Mile in ASP.NET MVC code and slides&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;I had a lot of people asking about the wrappers on top of our browser automation tool, WatiN, and if they were open source.&amp;#160; The answer is no, not yet, but we’re working on it.&amp;#160; A quick road to fragile UI tests is browser automation code directly in your UI tests, so the sample code provided should at least provide some ideas and guidance to craft your own wrappers.&amp;#160; And as always, my door is always open for questions.&amp;#160; My email door, at least.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;a href="http://www.dotnetkicks.com/kick/?title=Thanks+Houston+TechFest!&amp;url=http%3a%2f%2fwww.lostechies.com%2fblogs%2fjimmy_bogard%2farchive%2f2009%2f09%2f28%2fthanks-houston-techfest.aspx"&gt;&lt;img src="http://www.dotnetkicks.com/Services/Images/KickItImageGenerator.ashx?url=http%3a%2f%2fwww.lostechies.com%2fblogs%2fjimmy_bogard%2farchive%2f2009%2f09%2f28%2fthanks-houston-techfest.aspx" border="0" alt="Kick It on DotNetKicks.com" /&gt;&lt;/a&gt;&lt;img src="http://www.lostechies.com/aggbug.aspx?PostID=25606" width="1" height="1"&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/GrabBagOfT?a=UrhJgM5djrQ:DTHJv-TTGZA:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GrabBagOfT?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GrabBagOfT?a=UrhJgM5djrQ:DTHJv-TTGZA:V_sGLiPBpWU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GrabBagOfT?i=UrhJgM5djrQ:DTHJv-TTGZA:V_sGLiPBpWU" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/GrabBagOfT?a=UrhJgM5djrQ:DTHJv-TTGZA:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/GrabBagOfT?i=UrhJgM5djrQ:DTHJv-TTGZA:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/GrabBagOfT/~4/UrhJgM5djrQ" height="1" width="1"/&gt;</description><slash:comments xmlns:slash="http://purl.org/rss/1.0/modules/slash/">6</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://www.lostechies.com/blogs/jimmy_bogard/rsscomments.aspx?PostID=25606</wfw:commentRss><wfw:comment xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://www.lostechies.com/blogs/jimmy_bogard/commentapi.aspx?PostID=25606</wfw:comment><feedburner:origLink>http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/09/28/thanks-houston-techfest.aspx</feedburner:origLink></item></channel></rss>
