<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:georss="http://www.georss.org/georss" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:gd="http://schemas.google.com/g/2005" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" gd:etag="W/&quot;CUQHQnY7fCp7ImA9WxFaFUs.&quot;"><id>tag:blogger.com,1999:blog-8942599</id><updated>2010-07-19T14:15:33.804-04:00</updated><title>the urban canuk, eh</title><subtitle type="html">Living idyllically in a .NET, C#, TDD world</subtitle><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://www.bryancook.net/feeds/posts/default" /><link rel="alternate" type="text/html" href="http://www.bryancook.net/" /><link rel="next" type="application/atom+xml" href="http://www.blogger.com/feeds/8942599/posts/default?start-index=26&amp;max-results=25&amp;redirect=false&amp;v=2" /><author><name>bryan</name><uri>http://www.blogger.com/profile/01332614158223702009</uri><email>noreply@blogger.com</email></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>158</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/bryanbcook" /><feedburner:info uri="bryanbcook" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><feedburner:emailServiceId>bryanbcook</feedburner:emailServiceId><feedburner:feedburnerHostname>http://feedburner.google.com</feedburner:feedburnerHostname><feedburner:feedFlare href="http://add.my.yahoo.com/rss?url=http%3A%2F%2Ffeeds.feedburner.com%2Fbryanbcook" src="http://us.i1.yimg.com/us.yimg.com/i/us/my/addtomyyahoo4.gif">Subscribe with My Yahoo!</feedburner:feedFlare><feedburner:feedFlare href="http://www.newsgator.com/ngs/subscriber/subext.aspx?url=http%3A%2F%2Ffeeds.feedburner.com%2Fbryanbcook" src="http://www.newsgator.com/images/ngsub1.gif">Subscribe with NewsGator</feedburner:feedFlare><feedburner:feedFlare href="http://feeds.my.aol.com/add.jsp?url=http%3A%2F%2Ffeeds.feedburner.com%2Fbryanbcook" src="http://o.aolcdn.com/favorites.my.aol.com/webmaster/ffclient/webroot/locale/en-US/images/myAOLButtonSmall.gif">Subscribe with My AOL</feedburner:feedFlare><feedburner:feedFlare href="http://www.bloglines.com/sub/http://feeds.feedburner.com/bryanbcook" src="http://www.bloglines.com/images/sub_modern11.gif">Subscribe with Bloglines</feedburner:feedFlare><feedburner:feedFlare href="http://www.netvibes.com/subscribe.php?url=http%3A%2F%2Ffeeds.feedburner.com%2Fbryanbcook" src="http://www.netvibes.com/img/add2netvibes.gif">Subscribe with Netvibes</feedburner:feedFlare><feedburner:feedFlare href="http://fusion.google.com/add?feedurl=http%3A%2F%2Ffeeds.feedburner.com%2Fbryanbcook" src="http://buttons.googlesyndication.com/fusion/add.gif">Subscribe with Google</feedburner:feedFlare><feedburner:feedFlare href="http://www.pageflakes.com/subscribe.aspx?url=http%3A%2F%2Ffeeds.feedburner.com%2Fbryanbcook" src="http://www.pageflakes.com/ImageFile.ashx?instanceId=Static_4&amp;fileName=ATP_blu_91x17.gif">Subscribe with Pageflakes</feedburner:feedFlare><feedburner:feedFlare href="http://www.plusmo.com/add?url=http%3A%2F%2Ffeeds.feedburner.com%2Fbryanbcook" src="http://plusmo.com/res/graphics/fbplusmo.gif">Subscribe with Plusmo</feedburner:feedFlare><feedburner:feedFlare href="http://my.feedlounge.com/external/subscribe?url=http%3A%2F%2Ffeeds.feedburner.com%2Fbryanbcook" src="http://static.feedlounge.com/buttons/subscribe_0.gif">Subscribe with FeedLounge</feedburner:feedFlare><feedburner:feedFlare href="http://www.live.com/?add=http%3A%2F%2Ffeeds.feedburner.com%2Fbryanbcook" src="http://tkfiles.storage.msn.com/x1piYkpqHC_35nIp1gLE68-wvzLZO8iXl_JMledmJQXP-XTBOLfmQv4zhj4MhcWEJh_GtoBIiAl1Mjh-ndp9k47If7hTaFno0mxW9_i3p_5qQw">Subscribe with Live.com</feedburner:feedFlare><feedburner:browserFriendly>Hey! Your interest in my blog is really appreciated. Thanks! Please feel free to add your voice to the conversation by leaving comments on posts that interest you.</feedburner:browserFriendly><entry gd:etag="W/&quot;D0QNQXg9fyp7ImA9WxBWF08.&quot;"><id>tag:blogger.com,1999:blog-8942599.post-627724310942284622</id><published>2010-02-09T08:50:00.000-05:00</published><updated>2010-02-09T09:23:10.667-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-02-09T09:23:10.667-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term=".net" /><title>Running code in a separate AppDomain</title><content type="html">&lt;p&gt;Suppose you’ve got a chunk of code that you need to run as part of your application but you’re concerned that it might bring down your app or introduce a memory leak.&amp;#160; Fortunately, the .NET runtime provides an easy mechanism to run arbitrary code in a separate AppDomain.&amp;#160; Not only can you isolate all exceptions to that AppDomain, but when the AppDomain unloads you can reclaim all the memory that was consumed.&lt;/p&gt;  &lt;p&gt;Here’s a quick walkthrough that demonstrates creating an AppDomain and running some isolated code.&lt;/p&gt;  &lt;h3&gt;Create a new AppDomain&lt;/h3&gt;  &lt;p&gt;First we’ll create a new AppDomain based off the information of the currently running AppDomain.&lt;/p&gt;  &lt;pre class="brush: csharp;"&gt;AppDomainSetup currentSetup = AppDomain.CurrentDomain.SetupInformation;

var info = new AppDomainSetup()
              {
                  ApplicationBase = currentSetup.ApplicationBase,
                  LoaderOptimization = currentSetup.LoaderOptimization
              };

var domain = AppDomain.CreateDomain(&amp;quot;Widget Domain&amp;quot;, null, info);&lt;/pre&gt;

&lt;h3&gt;Unwrap your MarshalByRefObject&lt;/h3&gt;

&lt;p&gt;Next we’ll create an object in that AppDomain and serialize a handle to it so that we can control the code in the remote AppDomain.&amp;#160; It’s important to make sure the object you’re creating inherits from &lt;em&gt;MarshalByRefObject&lt;/em&gt; and is marked as serializable.&amp;#160; If you forget this step, the entire object will serialize over to the original AppDomain and you lose all isolation.&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;string assemblyName = &amp;quot;AppDomainExperiment&amp;quot;;
string typeName = &amp;quot;AppDomainExperiment.MemoryEatingWidget&amp;quot;;

IWidget widget = (IWidget)domain.CreateInstanceAndUnwrap(assemblyName, typeName);&lt;/pre&gt;

&lt;h3&gt;Unload the domain&lt;/h3&gt;

&lt;p&gt;Once we’ve finished with the object, we can broom the entire AppDomain which frees up all resources attached to it.&amp;#160; In the example below, I’ve deliberately created a static reference to an object to prevent it from going out of scope.&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;AppDomain.Unload(domain);&lt;/pre&gt;

&lt;h3&gt;Putting it all together&lt;/h3&gt;

&lt;p&gt;Here’s a sample that shows all the moving parts.&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;namespace AppDomainExperiment
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using Microsoft.VisualStudio.TestTools.UnitTesting;

    [Test]
    public class AppDomainLoadTests
    {
        [TestMethod]
        public void RunMarshalByRefObjectInSeparateAppDomain()
        {
            Console.WriteLine(&amp;quot;Executing in AppDomain: {0}&amp;quot;, AppDomain.CurrentDomain.Id);
            WriteMemory(&amp;quot;Before creating the runner&amp;quot;);

            using(var runner = new WidgetRunner(&amp;quot;AppDomainExperiment&amp;quot;,
                                                &amp;quot;AppDomainExperiment.MemoryEatingWidget&amp;quot;))
            {

                WriteMemory(&amp;quot;After creating the runner&amp;quot;);

                runner.Run(Console.Out);

                WriteMemory(&amp;quot;After executing the runner&amp;quot;);
            }

            WriteMemory(&amp;quot;After disposing the runner&amp;quot;);
        }

        private static void WriteMemory(string where)
        {
            GC.Collect();
            GC.WaitForPendingFinalizers();
            long memory = GC.GetTotalMemory(false);

            Console.WriteLine(&amp;quot;Memory used '{0}': {1}&amp;quot;, where, memory.ToString());
        }
    }

    public interface IWidget
    {
        void Run(TextWriter writer);
    }

    public class WidgetRunner
    {
        private readonly string _assemblyName;
        private readonly string _typeName;
        private AppDomain _domain;

        public WidgetRunner(string assemblyName, string typeName)
        {
            _assemblyName = assemblyName;
            _typeName = typeName;
        }

        #region IWidget Members

        public void Run(TextWriter writer)
        {
            AppDomainSetup currentSetup = AppDomain.CurrentDomain.SetupInformation;

            var info = new AppDomainSetup()
                          {
                              ApplicationBase = currentSetup.ApplicationBase,
                              LoaderOptimization = currentSetup.LoaderOptimization
                          };

            _domain = AppDomain.CreateDomain(&amp;quot;Widget Domain&amp;quot;, null, info);

            var widget = (IWidget)_domain.CreateInstanceAndUnwrap(_assemblyName, _typeName);

            if (!(widget is MarshalByRefObject))
            {
                throw new NotSupportedException(&amp;quot;Widget must be MarshalBeRefObject&amp;quot;);
            }
            widget.Run(writer);
        }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            GC.SuppressFinalize(this);
            AppDomain.Unload(_domain);
        }

        #endregion
    }

    [Serializable]
    public class MemoryEatingWidget : MarshalByRefObject, IWidgetModule
    {
        private IList&amp;lt;string&amp;gt; _memoryEater;

        private static IWidgetModule Instance;

        #region IAppLauncher Members

        public void Run(TextWriter writer)
        {
            writer.WriteLine(&amp;quot;Executing in AppDomain: {0}&amp;quot;, AppDomain.CurrentDomain.Id);

            _memoryEater = new List&amp;lt;string&amp;gt;();

            // create some really big strings
            for(int i = 0; i &amp;lt; 100; i++)
            {
                var s = new String('c', i*100000);
                _memoryEater.Add(s);
            }

            // THIS SHOULD PREVENT THE MEMORY FROM BEING GC'd
            Instance = this;
        }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            
        }

        #endregion
    }
}&lt;/pre&gt;

&lt;p&gt;Running the test shows the following output:&lt;/p&gt;

&lt;pre&gt;Executing in AppDomain: 2
Memory used 'Before creating the runner': 569060
Memory used 'After creating the runner': 487508
Executing in AppDomain: 3
Memory used 'After executing the runner': 990525340
Memory used 'After disposing the runner': 500340&lt;/pre&gt;

&lt;p&gt;Based on this output, the main take away is that the memory is reclaimed when the AppDomain is unloaded.&amp;#160; Why do the numbers not match up in the beginning and end?&amp;#160; It’s one of those mysteries of the managed garbage collector, it reminds me of my favorite Norm McDonald joke from SNL:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;“Who are safer drivers? Men, or women?? Well, according to a new survey, 55% of adults feel that women are most responsible for minor fender-benders, while 78% blame men for most fatal crashes. Please note that the percentages in these pie graphs do not add up to 100% because the math was done by a woman. [Crowd groans.] For those of you hissing at that joke, it should be noted that that joke was written by a woman. So, now you don't know what the hell to do, do you? [Laughter] Nah, I'm just kidding, we don't hire women”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Happy Coding.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8942599-627724310942284622?l=www.bryancook.net' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/ficIj5VoEs0lOhKYI-dH5ir_1I4/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/ficIj5VoEs0lOhKYI-dH5ir_1I4/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/ficIj5VoEs0lOhKYI-dH5ir_1I4/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/ficIj5VoEs0lOhKYI-dH5ir_1I4/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=74lp-OW5oIA:DaFnDaAYnCk:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=74lp-OW5oIA:DaFnDaAYnCk:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=74lp-OW5oIA:DaFnDaAYnCk:G79ilh31hkQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=G79ilh31hkQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=74lp-OW5oIA:DaFnDaAYnCk:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?i=74lp-OW5oIA:DaFnDaAYnCk:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bryanbcook/~4/74lp-OW5oIA" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.bryancook.net/feeds/627724310942284622/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=8942599&amp;postID=627724310942284622" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8942599/posts/default/627724310942284622?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8942599/posts/default/627724310942284622?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/bryanbcook/~3/74lp-OW5oIA/running-code-in-separate-appdomain.html" title="Running code in a separate AppDomain" /><author><name>bryan</name><uri>http://www.blogger.com/profile/01332614158223702009</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="09074999001888279291" /></author><thr:total>2</thr:total><feedburner:origLink>http://www.bryancook.net/2010/02/running-code-in-separate-appdomain.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEAEQXk_fyp7ImA9WxBWFk4.&quot;"><id>tag:blogger.com,1999:blog-8942599.post-1530095706704187397</id><published>2010-02-08T08:45:00.000-05:00</published><updated>2010-02-08T08:45:00.747-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-02-08T08:45:00.747-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term=".net" /><category scheme="http://www.blogger.com/atom/ns#" term="twelve-days-of-code" /><title>Twelve Days of Code – Wrap up</title><content type="html">&lt;p&gt;Well it’s been a very long twelve days indeed, and I accomplished more than I thought I would.&amp;#160; But alas, all good things must come to end, so after a short hiatus on the blog I’m back to close out the Twelve Days of Code series for 2009.&lt;/p&gt;  &lt;p&gt;For your convenience, here’s a list of the posts:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;a href="http://www.bryancook.net/2009/12/twelve-days-of-code-challenge-2009.html" target="_blank"&gt;Overview&lt;/a&gt;&lt;/li&gt;    &lt;li&gt;&lt;a href="http://www.bryancook.net/2009/12/twelve-days-solution-setup.html" target="_blank"&gt;Solution Setup&lt;/a&gt;&lt;/li&gt;    &lt;li&gt;&lt;a href="http://www.bryancook.net/2009/12/twelve-days-of-code-pomodoro-object.html" target="_blank"&gt;Object Model&lt;/a&gt;&lt;/li&gt;    &lt;li&gt;&lt;a href="http://www.bryancook.net/2009/12/twelve-days-of-code-views.html" target="_blank"&gt;Views&lt;/a&gt;&lt;/li&gt;    &lt;li&gt;&lt;a href="http://www.bryancook.net/2009/12/twelve-days-of-code-windows-7-shell.html" target="_blank"&gt;Windows 7 Shell Integration&lt;/a&gt;&lt;/li&gt;    &lt;li&gt;&lt;a href="http://www.bryancook.net/2010/01/twelve-days-of-code-entity-framework-40.html" target="_blank"&gt;Entity Framework&lt;/a&gt;&lt;/li&gt;    &lt;li&gt;&lt;a href="http://www.bryancook.net/2010/01/twelve-days-of-code-unity-framework.html" target="_blank"&gt;Unity Framework&lt;/a&gt;&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;I want to thank all those who showed interest in the concept and if there are folks out there who were following along at home, please drop me a line or a comment.&lt;/p&gt;  &lt;p&gt;For those interested in seeing some of the .NET 4.0 code and extending my work, the &lt;a href="http://code.google.com/p/snippetware/source/browse/#svn/trunk/src/TwelveDays-2009" target="_blank"&gt;code is available for download&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;I may pick up the experiment again once the next release candidate for Visual Studio is released.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8942599-1530095706704187397?l=www.bryancook.net' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/XXtKGmCaCD7CO_ssshk8SG86ofE/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/XXtKGmCaCD7CO_ssshk8SG86ofE/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/XXtKGmCaCD7CO_ssshk8SG86ofE/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/XXtKGmCaCD7CO_ssshk8SG86ofE/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=rcT2WgrOdDw:6V51ymXxmPU:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=rcT2WgrOdDw:6V51ymXxmPU:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=rcT2WgrOdDw:6V51ymXxmPU:G79ilh31hkQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=G79ilh31hkQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=rcT2WgrOdDw:6V51ymXxmPU:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?i=rcT2WgrOdDw:6V51ymXxmPU:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bryanbcook/~4/rcT2WgrOdDw" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.bryancook.net/feeds/1530095706704187397/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=8942599&amp;postID=1530095706704187397" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8942599/posts/default/1530095706704187397?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8942599/posts/default/1530095706704187397?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/bryanbcook/~3/rcT2WgrOdDw/twelve-days-of-code-wrap-up.html" title="Twelve Days of Code – Wrap up" /><author><name>bryan</name><uri>http://www.blogger.com/profile/01332614158223702009</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="09074999001888279291" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.bryancook.net/2010/02/twelve-days-of-code-wrap-up.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkYGQ3c-fSp7ImA9WxBQE0k.&quot;"><id>tag:blogger.com,1999:blog-8942599.post-4152429959031260317</id><published>2010-01-12T18:48:00.001-05:00</published><updated>2010-01-12T18:48:42.955-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-01-12T18:48:42.955-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term=".net" /><category scheme="http://www.blogger.com/atom/ns#" term="TDD" /><title>The Three Step Developer Flow</title><content type="html">&lt;p&gt;A long time ago, a mentor of mine passed on some good advise for developers that has stuck well with me: “Make it work.&amp;#160; Make it right.&amp;#160; Make it fast.”&amp;#160; While this simple mantra is likely influenced by &lt;a href="http://en.wikipedia.org/wiki/Program_optimization" target="_blank"&gt;Donald Knuth’s famous and misquoted statement that “&lt;em&gt;premature optimization is the root of all evil&lt;/em&gt;”&lt;/a&gt;, it’s more about how a developer should approach development altogether.&lt;/p&gt;  &lt;h3&gt;Breaking it down…&lt;/h3&gt;  &lt;p&gt;What I’ve always loved about this simple advice is that if a developer takes the steps out of order, such as putting emphasis on design or performance, there’s a very strong possibility that the code will never work.&lt;/p&gt;  &lt;h4&gt;Make it work…&lt;/h4&gt;  &lt;p&gt;Developers should take the most pragmatic solution possible to get the solution to work.&amp;#160; In some cases this should be considered prototype code that should be thrown away before going into production.&amp;#160; &lt;em&gt;Sadly, I’m sure that 80% of all production code is prototype code with unrealized design.&lt;/em&gt;&lt;/p&gt;  &lt;h4&gt;Make it right…&lt;/h4&gt;  &lt;p&gt;Now that you know you know how to get the code to work, take some time to get into a shape that you can live with.&amp;#160; Interesting enough, emphasis should not be placed on designing for performance at this point.&amp;#160; If you can’t get to this stage, it should be considered technical debt to be resolved later.&lt;/p&gt;  &lt;h4&gt;Make it fast….&lt;/h4&gt;  &lt;p&gt;At this point you should have working code that looks clean and elegant, but how does it stack up when it’s integrated with production components or put under load?&amp;#160; If you had spent any time in the last two steps optimizing the code to handle load of a thousand users but it’s only called once in the application – you may have wasted your time and optimized prematurely.&amp;#160; To truly know, code should be examined under a profiler to determine if the code meets the performance goals of the application.&amp;#160; This is all a part of embedding a “&lt;a href="http://blogs.msdn.com/ricom/" target="_blank"&gt;culture of Performance&lt;/a&gt;” into your organization. &lt;/p&gt;  &lt;h3&gt;Aligned with Test Driven Development&lt;/h3&gt;  &lt;p&gt;It’s interesting that this concept overlaps with Test Driven Development’s mantra “Red, Green, Refactor” quite well.&amp;#160; Rather than developing prototype code as a console app, I write tests to prove that it works.&amp;#160; When it’s time to clean up the code and make it right, I’m refactoring both the tests and the code in small increments – after each change, I can verify that it still works.&amp;#160; &lt;/p&gt;  &lt;p&gt;Later, if we identify performance issues with the code, I can use the tests as production assets to help me understand what the code needs to do.&amp;#160; This provides guidance when ripping out chunks of poorly performing code.&lt;/p&gt;  &lt;p&gt;By following either the “red / green / refactor” or “make it work / right / fast&amp;quot; mantras mean that I don’t incorporate best practices or obvious implementation when writing the code? Hardly. I’ll write what I think needs to be written, but it’s important not to get carried away.&lt;/p&gt;  &lt;p&gt;Write tests.&amp;#160; Test often.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8942599-4152429959031260317?l=www.bryancook.net' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/Ksikea9PmTLDm38dPWmMgAR94Go/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/Ksikea9PmTLDm38dPWmMgAR94Go/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/Ksikea9PmTLDm38dPWmMgAR94Go/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/Ksikea9PmTLDm38dPWmMgAR94Go/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=YTS-HlcdNpg:RSzKKWsg5RA:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=YTS-HlcdNpg:RSzKKWsg5RA:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=YTS-HlcdNpg:RSzKKWsg5RA:G79ilh31hkQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=G79ilh31hkQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=YTS-HlcdNpg:RSzKKWsg5RA:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?i=YTS-HlcdNpg:RSzKKWsg5RA:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bryanbcook/~4/YTS-HlcdNpg" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.bryancook.net/feeds/4152429959031260317/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=8942599&amp;postID=4152429959031260317" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8942599/posts/default/4152429959031260317?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8942599/posts/default/4152429959031260317?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/bryanbcook/~3/YTS-HlcdNpg/three-step-developer-flow.html" title="The Three Step Developer Flow" /><author><name>bryan</name><uri>http://www.blogger.com/profile/01332614158223702009</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="09074999001888279291" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.bryancook.net/2010/01/three-step-developer-flow.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CE4DRH0yfip7ImA9WxBRGU0.&quot;"><id>tag:blogger.com,1999:blog-8942599.post-4760363938440994981</id><published>2010-01-07T17:21:00.001-05:00</published><updated>2010-01-07T17:22:55.396-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-01-07T17:22:55.396-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term=".net" /><category scheme="http://www.blogger.com/atom/ns#" term="twelve-days-of-code" /><title>Twelve Days of Code – Unity Framework</title><content type="html">&lt;p&gt;As part of the &lt;a href="http://www.bryancook.net/2009/12/twelve-days-of-code-challenge-2009.html"&gt;twelve days of code&lt;/a&gt;, I’m building a Pomodoro style task tracking application and blogging about it. This post is the seventh in this series. This post focuses on using the Microsoft Unity Framework in our .NET 4.0 application.&lt;/p&gt;  &lt;p&gt;This post assumes assumes that you are familiar with the last few posts and have a working understanding of Inversion of Control.&amp;#160; If you’re new to this series, please check out some of the &lt;a href="http://www.bryancook.net/search/label/twelve-days-of-code" target="_blank"&gt;previous posts&lt;/a&gt; and then come back.&amp;#160; If you’ve never worked with Inversion of Control, it’s primarily about decoupling objects from their implementation (Factory Pattern, Dependency Injection, Service Locator) – but the best place to start is probably &lt;a href="http://martinfowler.com/articles/injection.html" target="_blank"&gt;here&lt;/a&gt;.&lt;/p&gt;  &lt;h3&gt;Overview&lt;/h3&gt;  &lt;p&gt;Our goal for this post is to remove as many hard-code references to types as possible.&amp;#160; Currently, the constructor of our MainWindow initializes all the dependencies of the Model and then manually sets the DataContext.&amp;#160; We need to pull this out of the constructor and decouple the binding from TaskApplicationViewModel to ITaskApplication.&lt;/p&gt;  &lt;h3&gt;Initializing the Container&lt;/h3&gt;  &lt;p&gt;We’ll introduce our inversion of control container in the Application’s &lt;em&gt;OnStartup&lt;/em&gt; event.&amp;#160; The container’s job is to provide type location and object lifetime management for our objects, so all the hard-coded initialization logic in the &lt;em&gt;MainWindow&lt;/em&gt; constructor is registered here.&lt;/p&gt;  &lt;pre class="brush: csharp;"&gt;public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        IUnityContainer container = new UnityContainer();
        
        container.RegisterType&amp;lt;ITaskApplication, TaskApplicationViewModel&amp;gt;();
        container.RegisterType&amp;lt;ITaskSessionController, TaskSessionController&amp;gt;();
        container.RegisterType&amp;lt;IAlarmController, TaskAlarmController&amp;gt;();
        container.RegisterType&amp;lt;ISessionRepository, TaskSessionRepository&amp;gt;();

        Window window = new MainWindow();
        window.DataContext = container.Resolve&amp;lt;ITaskApplication&amp;gt;();
        window.Show();

        
        base.OnStartup(e);
    }
}&lt;/pre&gt;

&lt;p&gt;Also note, we’re initializing the &lt;em&gt;MainWindow&lt;/em&gt; with our data context and then displaying the window.&amp;#160; This small change means that we must remove the &lt;em&gt;MainWindow.xaml&lt;/em&gt; reference in the &lt;em&gt;App.xaml&lt;/em&gt; (otherwise we’ll launch two windows).&lt;/p&gt;

&lt;h3&gt;Next Steps&lt;/h3&gt;

&lt;p&gt;Aside from the simplified object construction, it would appear that the above code doesn’t buy us much: we have roughly the same number of lines of code and we are simply delegating objection construction to Unity.&amp;#160; Alternatively, we could move the hard-coded registrations to a configuration file (though that’s not my preference here).&lt;/p&gt;

&lt;p&gt;In the next post, we’ll see how using Prism’s Bootstrapper and Module configuration allows us to move this logic into their own modules.&lt;/p&gt;

&lt;p&gt;In the next step, we’ll look at using P&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8942599-4760363938440994981?l=www.bryancook.net' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/wh3dz5Sa66rqjvJt38-ZYtGMW8o/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/wh3dz5Sa66rqjvJt38-ZYtGMW8o/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/wh3dz5Sa66rqjvJt38-ZYtGMW8o/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/wh3dz5Sa66rqjvJt38-ZYtGMW8o/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=8bULRuyTQFU:E52s7Qi_5SE:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=8bULRuyTQFU:E52s7Qi_5SE:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=8bULRuyTQFU:E52s7Qi_5SE:G79ilh31hkQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=G79ilh31hkQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=8bULRuyTQFU:E52s7Qi_5SE:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?i=8bULRuyTQFU:E52s7Qi_5SE:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bryanbcook/~4/8bULRuyTQFU" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.bryancook.net/feeds/4760363938440994981/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=8942599&amp;postID=4760363938440994981" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8942599/posts/default/4760363938440994981?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8942599/posts/default/4760363938440994981?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/bryanbcook/~3/8bULRuyTQFU/twelve-days-of-code-unity-framework.html" title="Twelve Days of Code – Unity Framework" /><author><name>bryan</name><uri>http://www.blogger.com/profile/01332614158223702009</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="09074999001888279291" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.bryancook.net/2010/01/twelve-days-of-code-unity-framework.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUcDQXk4fSp7ImA9WxBRGU0.&quot;"><id>tag:blogger.com,1999:blog-8942599.post-2447682538220407110</id><published>2010-01-02T20:00:00.001-05:00</published><updated>2010-01-07T17:24:30.735-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-01-07T17:24:30.735-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term=".net" /><category scheme="http://www.blogger.com/atom/ns#" term="twelve-days-of-code" /><title>Twelve Days of Code – Entity Framework 4.0</title><content type="html">&lt;p&gt;As part of the &lt;a href="http://www.bryancook.net/2009/12/twelve-days-of-code-challenge-2009.html"&gt;twelve days of code&lt;/a&gt;, I’m building a Pomodoro style task tracking application and blogging about it. This post is the sixth in this series. Today I’ll be adding some persistence logic to the Pomodoro application.&lt;/p&gt;  &lt;p&gt;I should point I’ve never been a huge fan of object-relational mapping tools, and while I’ve done a few tours with SQL Server I haven’t played much with SQLite.&amp;#160; I’ve heard good things about the upcoming version of the Entity Framework in .NET 4.0, so this post gives me a chance to play with both.&lt;/p&gt;  &lt;h3&gt;Getting Ready&lt;/h3&gt;  &lt;p&gt;As SQLite isn’t one of the default providers supported by Visual Studio 2010 (Beta 2), I &lt;a href="http://sourceforge.net/projects/sqlite-dotnet2/files/SQLite%20for%20ADO.NET%202.0/" target="_blank"&gt;downloaded and installed&lt;/a&gt; &lt;a href="http://sqlite.phxsoftware.com/" target="_blank"&gt;SQLite&lt;/a&gt;.&amp;#160; The installation adds the SQLite libraries to the Global Assembly Cache, and adds Designer Support for connecting to a SQLite database through the &lt;em&gt;Server Explorer&lt;/em&gt;.&amp;#160; The installation and GAC’d assemblies may prove to be an issue later when we want to deploy the application, but we’ll worry about that later.&lt;/p&gt;  &lt;h3&gt;Creating a Session Repository&lt;/h3&gt;  &lt;p&gt;So far, the project structure has a “Core” and “Shell” project where the “Core” project contains the central interfaces for the application.&amp;#160; Since the ITaskSessionController already has the responsibility of handling starting and stopping of sessions, it is the ideal candidate to interact with a &lt;em&gt;ISessionRepository&lt;/em&gt; for recording these activities against a persistent store.&lt;/p&gt;  &lt;p&gt;To handle auditing of sessions, I created a ISessionRepository interface which lives in the Core library:&lt;/p&gt;  &lt;pre class="brush: csharp; gutter: false;"&gt;interface ISessionRepository
{
    void RecordCompletion(ITaskSession session);
    void RecordCancellation(ITaskSession session);
}&lt;/pre&gt;

&lt;p&gt;Although we don't have an implementation for this interface, we do know how that an object with this signature will be used by the TaskSessionController.&amp;#160; In anticipation of these changes, we add tests to the &lt;em&gt;TaskSessionController&lt;/em&gt; that verify it communicates with its dependency:&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;public TaskSessionControllerSpecs : SpecFor&amp;lt;TaskSessionController&amp;gt;
{
  // ... initial context setup with Mock ISessionRepository
  // omitted for clarity

  public class when_a_session_completes : TaskSessionControllerSpecs
  {
     // omitted for clarity

     [TestMethod]
     public void ensure_activity_is_recorded_in_repository()
     {
         repositoryMock.Verify( 
            r =&amp;gt; r.RecordCompletion(
                    It.Is&amp;lt;ITaskSession&amp;gt;( (s) =&amp;gt; s == session )
                        );
     }
  }
}&lt;/pre&gt;

&lt;p&gt;To ensure the tests pass, we extend the &lt;em&gt;TaskSessionController&lt;/em&gt; to take a &lt;em&gt;ISessionRepository&lt;/em&gt; in its constructor and add the appropriate implementation.&amp;#160; Naturally, because the constructor of the &lt;em&gt;TaskSessionController&lt;/em&gt; has changed, we adjust the fixture setup so that the code will compile.&amp;#160; Below is a snippet of modified &lt;em&gt;TaskSessionController&lt;/em&gt;:&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;public class TaskSessionController : ITaskSessionController
{
    public TaskSessionController(ISessionRepository repository)
    {
        SessionRepository = repository;
    }

    protected ISessionRepository SessionRepository
    {
        get; set;
    }

    public void Finish(ITaskSession session)
    {
        session.Stop()

        SessionRepository.RecordCompletion(session);
    }

    // ...omitted for clarity
}&lt;/pre&gt;

&lt;h3&gt;Adding ADO.NET Entity Framework to the Project&lt;/h3&gt;

&lt;p&gt;While we could add the implementation of the &lt;em&gt;ISessionRepository&lt;/em&gt; into the Core library, I’m going to add a new library &lt;em&gt;Pomodoro.Data where&lt;/em&gt; we’ll add the Entity Framework model.&amp;#160; This strategy allows us to extend the Core model and provides us with some freedom to create alternate persistence strategies simply by swapping out the Pomodoro.Data assembly.&lt;/p&gt;

&lt;p&gt;Once the project is created, we add the Entity Framework to the project using the Add New “ADO.NET Entity Data Model” and follow the wizard:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://lh5.ggpht.com/_XB6P2gWz--8/Sz_sDfc4tfI/AAAAAAAAAdk/SgWzF5DFBbo/s1600-h/Pomodoro.Data3.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="Pomodoro.Data" border="0" alt="Pomodoro.Data" src="http://lh3.ggpht.com/_XB6P2gWz--8/Sz_sD9VsoNI/AAAAAAAAAdo/IOM91ket25o/Pomodoro.Data_thumb1.png?imgmax=800" width="644" height="434" /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note, that the wizard adds the appropriate references to the project automatically.&amp;#160; &lt;/p&gt;

&lt;p&gt;Since we don’t have a working database, we’ll choose to create an Empty Model.&amp;#160; Later on, we’ll generate the database from our defined model.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://lh3.ggpht.com/_XB6P2gWz--8/Sz_sEsCt-yI/AAAAAAAAAds/VGQBBnnfqzc/s1600-h/emptymodel3.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="empty-model" border="0" alt="empty-model" src="http://lh3.ggpht.com/_XB6P2gWz--8/Sz_sFJvvH0I/AAAAAAAAAdw/PpWHV_6j2jk/emptymodel_thumb1.png?imgmax=800" width="471" height="484" /&gt;&lt;/a&gt;&amp;#160; &lt;/p&gt;

&lt;h3&gt;Creating a Data Model&lt;/h3&gt;

&lt;p&gt;One of the new features of the Entity Framework 4.0 is that it allows you to bind to an existing data model.&amp;#160; Although the TaskSession could be considered as a candidate for an existing model, it doesn’t fit the bill cleanly – Sessions represent countdown timers and they don’t track the final outcome.&amp;#160; Instead, we’ll use the default behavior of the framework and manually generate a model class, Task&lt;em&gt;SessionRecord:&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://lh6.ggpht.com/_XB6P2gWz--8/Sz_sH603p4I/AAAAAAAAAd0/NpPCQkRFjrM/s1600-h/addnewentity3.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="add-new-entity" border="0" alt="add-new-entity" src="http://lh3.ggpht.com/_XB6P2gWz--8/Sz_sIT-6h8I/AAAAAAAAAd4/0zJDCoPuHB0/addnewentity_thumb1.png?imgmax=800" width="644" height="384" /&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;For our auditing purposes, we only need to record an ID, Start and End Times and whether the session was completed or cancelled.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://lh5.ggpht.com/_XB6P2gWz--8/Sz_sIxea05I/AAAAAAAAAd8/V8chYKFElsk/s1600-h/task-session-record%5B5%5D.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="task-session-record" border="0" alt="task-session-record" src="http://lh6.ggpht.com/_XB6P2gWz--8/Sz_sJDs2pEI/AAAAAAAAAeA/UJE35bYhEY4/task-session-record_thumb%5B3%5D.png?imgmax=800" width="240" height="235" /&gt;&lt;/a&gt; &lt;/p&gt;

&lt;h3&gt;Creating the Database from the Model &lt;/h3&gt;

&lt;p&gt;&lt;em&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;After the model is complete, we generate the database from the model:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Right click the designer and choose “Model Browser” &lt;/li&gt;

  &lt;li&gt;In the Data Store, choose “Generate Database from Model” &lt;/li&gt;

  &lt;li&gt;Create a new database connection.&amp;#160; In our case, we specify SQLite provider &lt;/li&gt;

  &lt;li&gt;Finish our the wizard by clicking Next and Finish. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="http://lh3.ggpht.com/_XB6P2gWz--8/Sz_sJikZfTI/AAAAAAAAAeE/2Rb4Yw0uMKI/s1600-h/new-database-connection.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="new-database-connection" border="0" alt="new-database-connection" src="http://lh3.ggpht.com/_XB6P2gWz--8/Sz_sKXLSWfI/AAAAAAAAAeI/BITY3TuXX7Y/new-database-connection_thumb.png?imgmax=800" width="474" height="480" /&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;The wizard produces the Database and the matching DDL file to generate the tables.&amp;#160; &lt;em&gt;Note that SQLite must be installed in order to have it appear as an option in the wizard.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;Unfortunately, I wasn’t able to create the SQLite database using any of the tools with Visual Studio.&amp;#160; Instead, I cheated and manually created the TaskSessionRecord table.&amp;#160; We’ll hang onto the generated ddl file because we may want to programmatically generate the database at some point.&amp;#160; For the time being, I’ll cheat and copy the database to the bin\Debug folder.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;Implementing the Repository&lt;/h3&gt;

&lt;p&gt;The repository implementation is fairly straightforward.&amp;#160; We simply instantiate the object context (we specified part of the name when we added the ADO.NET Entity Data Model to the project), add a new TaskSessionRecord to the EntitySet and then save the changes to commit the transaction:&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;namespace Pomodoro.Data
{
    public class TaskSessionRepository : ISessionRepository
    {
        public void RecordCompletion(ITaskSession session)
        {
            using (var context = new PomodoroDataContainer())
            {
                context.TaskSessionRecords.AddObject(new TaskSessionRecord(session, true));
                context.SaveChanges();
            }
        }

        public void RecordCancellation(ITaskSession session)
        {
            using (var context = new PomodoroDataContainer())
            {
                context.TaskSessionRecords.AddObject(new TaskSessionRecord(session, false));
                context.SaveChanges();
            }
        }
    }
}&lt;/pre&gt;

&lt;p&gt;Note that to simplify the code, I extended the auto generated &lt;em&gt;TaskSessionRecord&lt;/em&gt; class to provide a convenience constructor.&amp;#160; Since the auto generated class is marked as &lt;em&gt;partial&lt;/em&gt;, the convenience constructor is placed in its own file.&amp;#160; As some of the existing generated code requires the presence of an empty constructor (&lt;a href="http://msdn.microsoft.com/en-us/library/k6sa6h87.aspx" target="_blank"&gt;which is implicitly defined by the compiler if not present&lt;/a&gt;), we must also include a default constructor.&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;public partial class TaskSessionRecord
{
    // needed to satisfy some of the existing generated code
    public TaskSessionRecord()
    {
    }

    public TaskSessionRecord(ITaskSession session, bool complete)
    {
        Id = session.Id;
        StartTime = session.StartTime;
        EndTime = session.EndTime;
        Complete = complete;
    }
}&lt;/pre&gt;

&lt;h3&gt;Integrating into the Shell&lt;/h3&gt;

&lt;p&gt;To integrate the new SessionRepository into the Pomodoro application we need to add the database, &lt;em&gt;Pomodoro.Data&lt;/em&gt; assembly and the appropriate configuration settings.&amp;#160; For the time being, we’ll add a reference to Pomodoro.Data library to the Shell application – this strategy may change if we introduce a composite application pattern such as Prism or MEF.&amp;#160; For brevity sake, I’ll manually copy the database into the &lt;em&gt;bin\Debug&lt;/em&gt; folder.&lt;/p&gt;

&lt;p&gt;The connection string settings appear in the app.config like so:&lt;/p&gt;

&lt;pre class="brush: xml;"&gt;&amp;lt;connectionStrings&amp;gt;
  &amp;lt;-- formatted for readibiliy --&amp;gt;
  &amp;lt;add name=&amp;quot;PomodoroDataContainer&amp;quot; 
       connectionString=&amp;quot;metadata=res://*/PomodoroData.csdl|res://*/PomodoroData.ssdl|res://*/PomodoroData.msl;
                    provider=System.Data.SQLite;
                    provider connection string=&amp;amp;quot;data source=Pomodoro.sqlite&amp;amp;quot;&amp;quot; 
       providerName=&amp;quot;System.Data.EntityClient&amp;quot; /&amp;gt;
&amp;lt;/connectionStrings&amp;gt;&lt;/pre&gt;

&lt;h3&gt;One Last Gotcha&lt;/h3&gt;

&lt;p&gt;As the solution is compiled against .NET Framework 4.0 and our Sqlite assemblies are compiled against .NET 2.0, we receive a really nasty error when the System.Data.SQLite assembly loads into the AppDomain:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;Mixed mode assembly is built against version 'v2.0.50727' of the runtime and cannot be loaded in the 4.0 runtime without additional configuration information&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We solve this problem by adding &lt;em&gt;useLegacyV2RuntimeActivationPolicy=&amp;quot;true&amp;quot;&lt;/em&gt; to the app.config:&lt;/p&gt;

&lt;pre class="brush: xml; gutter: false;"&gt;&amp;lt;startup useLegacyV2RuntimeActivationPolicy=&amp;quot;true&amp;quot;&amp;gt;
  &amp;lt;supportedRuntime version=&amp;quot;v4.0&amp;quot; sku=&amp;quot;.NETFramework,Version=v4.0&amp;quot;/&amp;gt;
&amp;lt;/startup&amp;gt;&lt;/pre&gt;

&lt;p&gt;&lt;/p&gt;

&lt;h3&gt;Next Steps&lt;/h3&gt;

&lt;p&gt;In the next post, we’ll look at adding Unity as a dependency injection container to the application.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8942599-2447682538220407110?l=www.bryancook.net' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/xk2lyWtLiy-DPlq7k94cgdh0W_g/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/xk2lyWtLiy-DPlq7k94cgdh0W_g/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/xk2lyWtLiy-DPlq7k94cgdh0W_g/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/xk2lyWtLiy-DPlq7k94cgdh0W_g/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=ySfz4adWN10:GPqn0npBBTk:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=ySfz4adWN10:GPqn0npBBTk:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=ySfz4adWN10:GPqn0npBBTk:G79ilh31hkQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=G79ilh31hkQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=ySfz4adWN10:GPqn0npBBTk:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?i=ySfz4adWN10:GPqn0npBBTk:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bryanbcook/~4/ySfz4adWN10" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.bryancook.net/feeds/2447682538220407110/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=8942599&amp;postID=2447682538220407110" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8942599/posts/default/2447682538220407110?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8942599/posts/default/2447682538220407110?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/bryanbcook/~3/ySfz4adWN10/twelve-days-of-code-entity-framework-40.html" title="Twelve Days of Code – Entity Framework 4.0" /><author><name>bryan</name><uri>http://www.blogger.com/profile/01332614158223702009</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="09074999001888279291" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.bryancook.net/2010/01/twelve-days-of-code-entity-framework-40.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Ak4BQXw5cCp7ImA9WxBREkU.&quot;"><id>tag:blogger.com,1999:blog-8942599.post-1637986703219010666</id><published>2009-12-31T14:44:00.000-05:00</published><updated>2009-12-31T14:49:10.228-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-12-31T14:49:10.228-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term=".net" /><category scheme="http://www.blogger.com/atom/ns#" term="twelve-days-of-code" /><title>Twelve Days of Code – Windows 7 Shell Integration</title><content type="html">&lt;p&gt;As part of the &lt;a href="http://www.bryancook.net/2009/12/twelve-days-of-code-challenge-2009.html"&gt;twelve days of code&lt;/a&gt;, I’m building a Pomodoro style task tracking application and blogging about it. This post is the fifth in this series.&amp;#160; Today I’ll cover adding some of the cool new Windows 7 Shell integration features in WPF 4.0.&lt;/p&gt;  &lt;p&gt;I’ll be revisiting some of the xaml and object model for this post, so it wouldn’t hurt to read up:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;a href="http://www.bryancook.net/2009/12/twelve-days-of-code-pomodoro-object.html" target="_blank"&gt;Twelve Days of Code – Pomodoro Object Model&lt;/a&gt; &lt;/li&gt;    &lt;li&gt;&lt;a href="http://www.bryancook.net/2009/12/twelve-days-of-code-views.html" target="_blank"&gt;Twelve Days of Code -- Views&lt;/a&gt; &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;WPF 4.0 offers some nice Windows 7 Shell integration that are easy to add to your application.&lt;/p&gt;  &lt;h3&gt;Progress State&lt;/h3&gt;  &lt;p&gt;Since the Pomdoro application’s primary function is to provide a countdown timer, it’s seems like a natural fit to use the built-in Windows 7 Taskbar progress indicator to show the time remaining.&amp;#160; Hooking it up was a snap.&amp;#160; The progress indicator uses two values &lt;em&gt;ProgressState&lt;/em&gt; and &lt;em&gt;ProgressValue&lt;/em&gt;, where &lt;em&gt;ProgressState&lt;/em&gt; indicates the progress mode (Error, Indeterminate, None, Normal, Paused) and &lt;em&gt;ProgressValue&lt;/em&gt; is a numeric value between 0 and 1.&amp;#160; Two simple converters provide the translation between ViewModel and View, one to control &lt;em&gt;ProgressState&lt;/em&gt; and the other to compute &lt;em&gt;ProgressValue&lt;/em&gt;.&lt;/p&gt;  &lt;pre class="brush: xml;"&gt;&amp;lt;Window.TaskbarItemInfo&amp;gt;
    &amp;lt;TaskbarItemInfo
        ProgressState=&amp;quot;{Binding ActiveItem, Converter={StaticResource ProgressStateConverter}}&amp;quot;
        ProgressValue=&amp;quot;{Binding ActiveItem, Converter={StaticResource ProgressValueConverter}}&amp;quot;
        &amp;gt;
    &amp;lt;/TaskbarItemInfo&amp;gt;
&amp;lt;/Window.TaskbarItemInfo&amp;gt;&lt;/pre&gt;

&lt;p&gt;Which looks something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://lh4.ggpht.com/_XB6P2gWz--8/Szwi0fAiYkI/AAAAAAAAAdQ/nFT2hqj14bs/s1600-h/taskbar-progress%5B4%5D.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="taskbar-progress" border="0" alt="taskbar-progress" src="http://lh6.ggpht.com/_XB6P2gWz--8/Szwi1JolpVI/AAAAAAAAAdY/__62vVJczOM/taskbar-progress_thumb%5B2%5D.png?imgmax=800" width="644" height="157" /&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Note that for &lt;em&gt;ProgressValue&lt;/em&gt; I’m binding to &lt;em&gt;ActiveItem&lt;/em&gt; instead of &lt;em&gt;TimeRemaining&lt;/em&gt;.&amp;#160; This is because the progress value is obtained through a percent complete calculation -- time remaining in the original session length – which requires that both values are available to the converter.&amp;#160; I suppose this could have been calculated through a multi-binding, but the single converter makes things much easier.&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;namespace Pomodoro.Shell.Converters
{
    [ValueConversion(typeof(ITaskSession), typeof(System.Windows.Shell.TaskbarItemProgressState))]
    public class ProgressStateConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if (value != null &amp;amp;&amp;amp; value is ITaskSession)
            {
                ITaskSession session = (ITaskSession)value;

                if (session.IsActive)
                {
                    return TaskbarItemProgressState.Normal;
                }
            }
            return TaskbarItemProgressState.None;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }


    [ValueConversion(typeof(ITaskSession), typeof(double))]
    public class ProgressValueConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if (value != null &amp;amp;&amp;amp; value is ITaskSession)
            {
                ITaskSession session = (ITaskSession)value;

                if (session.IsActive)
                {
                    int delta = session.SessionLength - session.TimeRemaining;
                    
                    return (double)(delta / (double)session.SessionLength);
                }
            }
            return 1;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}&lt;/pre&gt;

&lt;p&gt;This is all well and good, but there’s a minor setback: Progress will not increment unless the &lt;em&gt;PropertyChanged&lt;/em&gt; event is raised for the &lt;em&gt;ActiveItem&lt;/em&gt; property.&amp;#160; This is an easy fix: the &lt;em&gt;ITaskSession&lt;/em&gt; needs to expose a &lt;em&gt;NotifyProgress&lt;/em&gt; event and the &lt;em&gt;ITaskApplication&lt;/em&gt; needs to notify the View when the event fires.&amp;#160; Since the progress indicator in the taskbar is only a few dozen pixels wide, spamming the View with each millisecond update is a bit much.&amp;#160; We solve this problem by throttling the amount the event is raised using a &lt;em&gt;NotifyInterval &lt;/em&gt;property.&lt;/p&gt;

&lt;pre class="brush: csharp; collapse: true;"&gt;// from TaskSessionViewModel
void OnTimer(object sender, ElapsedEventArgs e)
{
    NotifyProperty(&amp;quot;TimeRemaining&amp;quot;);

    if (IsActive)
    {
        // if notify interval is set
        if (NotifyInterval &amp;gt; 0)
        {
            notifyIntervalCounter += TimerInterval;

            if (notifyIntervalCounter &amp;gt;= NotifyInterval)
            {
                if (NotifyProgress != null)
                {
                    NotifyProgress(this, EventArgs.Empty);
                }
                notifyIntervalCounter = 0;
            }
        }
    }

    if (TimeRemaining == 0)
    {
        // end timer
        IsActive = false;

        if (SessionFinished != null)
        {
            SessionFinished(this, EventArgs.Empty);
        }
    }
}&lt;/pre&gt;

&lt;h3&gt;TaskbarInfo Buttons&lt;/h3&gt;

&lt;p&gt;Using the exact same command bindings &lt;a href="http://www.bryancook.net/2009/12/twelve-days-of-code-views.html" target="_blank"&gt;mentioned in my last post&lt;/a&gt;, adding buttons to control the countdown timer from the AreoPeek window is dirt simple. The only gotcha is that the taskbar buttons do not support custom content, instead you must specify an image to display anything meaningful to the user.&lt;/p&gt;

&lt;pre class="brush: xml;"&gt;&amp;lt;Window ....&amp;gt;

    &amp;lt;Window.Resources&amp;gt;
        &amp;lt;!-- play icon for taskbar button --&amp;gt;
        &amp;lt;DrawingImage x:Key=&amp;quot;PlayImage&amp;quot;&amp;gt;
            &amp;lt;DrawingImage.Drawing&amp;gt;
                &amp;lt;DrawingGroup&amp;gt;
                    &amp;lt;DrawingGroup.Children&amp;gt;
                        &amp;lt;GeometryDrawing Brush=&amp;quot;Black&amp;quot; Geometry=&amp;quot;F1 M 50,25L 0,0L 0,50L 50,25 Z &amp;quot;/&amp;gt;
                    &amp;lt;/DrawingGroup.Children&amp;gt;
                &amp;lt;/DrawingGroup&amp;gt;
            &amp;lt;/DrawingImage.Drawing&amp;gt;
        &amp;lt;/DrawingImage&amp;gt;

        &amp;lt;!-- stop icon for taskbar button --&amp;gt;
        &amp;lt;DrawingImage x:Key=&amp;quot;StopImage&amp;quot;&amp;gt;
            &amp;lt;DrawingImage.Drawing&amp;gt;
                &amp;lt;DrawingGroup&amp;gt;
                    &amp;lt;DrawingGroup.Children&amp;gt;
                        &amp;lt;GeometryDrawing Brush=&amp;quot;Black&amp;quot; Geometry=&amp;quot;F1 M 0,0L 50,0L 50,50L 0,50L 0,0 Z &amp;quot;/&amp;gt;
                    &amp;lt;/DrawingGroup.Children&amp;gt;
                &amp;lt;/DrawingGroup&amp;gt;
            &amp;lt;/DrawingImage.Drawing&amp;gt;
        &amp;lt;/DrawingImage&amp;gt;

        // ... converters
        
    &amp;lt;/Window.Resources&amp;gt;

    &amp;lt;Window.TaskbarItemInfo&amp;gt;
        &amp;lt;TaskbarItemInfo ... &amp;gt;
            
            &amp;lt;TaskbarItemInfo.ThumbButtonInfos&amp;gt;
                &amp;lt;ThumbButtonInfoCollection&amp;gt;

                    &amp;lt;!-- start button --&amp;gt;
                    &amp;lt;ThumbButtonInfo
                        ImageSource=&amp;quot;{StaticResource ResourceKey=PlayImage}&amp;quot;
                        Command=&amp;quot;{Binding StartCommand}&amp;quot;
                        CommandParameter=&amp;quot;{Binding ActiveItem}&amp;quot;
                        Visibility=&amp;quot;{Binding ActiveItem.IsActive, 
                                     Converter={StaticResource BoolToHiddenConverter}, 
                                     FallbackValue={x:Static Member=pc:Visibility.Visible}}&amp;quot;
                        /&amp;gt;

                    &amp;lt;!-- stop button --&amp;gt;
                    &amp;lt;ThumbButtonInfo
                        ImageSource=&amp;quot;{StaticResource ResourceKey=StopImage}&amp;quot;
                        Command=&amp;quot;{Binding CancelCommand}&amp;quot;
                        CommandParameter=&amp;quot;{Binding ActiveItem}&amp;quot;
                        Visibility=&amp;quot;{Binding ActiveItem.IsActive, 
                                     Converter={StaticResource BoolToVisibleConverter}, 
                                     FallbackValue={x:Static Member=pc:Visibility.Collapsed}}&amp;quot;
                        /&amp;gt;
                &amp;lt;/ThumbButtonInfoCollection&amp;gt;
            &amp;lt;/TaskbarItemInfo.ThumbButtonInfos&amp;gt;
        &amp;lt;/TaskbarItemInfo&amp;gt;
    &amp;lt;/Window.TaskbarItemInfo&amp;gt;

    // ....

&amp;lt;/Window&amp;gt;&lt;/pre&gt;

&lt;p&gt;The applied XAML looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://lh5.ggpht.com/_XB6P2gWz--8/Szwi2ER8wRI/AAAAAAAAAdc/lYX-lkMDxKs/s1600-h/taskbar-buttons%5B3%5D.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="taskbar-buttons" border="0" alt="taskbar-buttons" src="http://lh4.ggpht.com/_XB6P2gWz--8/Szwi2zaYtKI/AAAAAAAAAdg/j2sCRH67kxE/taskbar-buttons_thumb%5B1%5D.png?imgmax=800" width="644" height="157" /&gt;&lt;/a&gt; &lt;/p&gt;

&lt;h3&gt;Next Steps&lt;/h3&gt;

&lt;p&gt;The next post we’ll add some auditing capability to the pomodoro application using SQLite and the Entity Framework version in .NET 4.0.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8942599-1637986703219010666?l=www.bryancook.net' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/T6abiSQ11xH2NDtcGYXdzkWoAvQ/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/T6abiSQ11xH2NDtcGYXdzkWoAvQ/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/T6abiSQ11xH2NDtcGYXdzkWoAvQ/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/T6abiSQ11xH2NDtcGYXdzkWoAvQ/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=DKYtLWhyqJc:A8siJQoqhKk:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=DKYtLWhyqJc:A8siJQoqhKk:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=DKYtLWhyqJc:A8siJQoqhKk:G79ilh31hkQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=G79ilh31hkQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=DKYtLWhyqJc:A8siJQoqhKk:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?i=DKYtLWhyqJc:A8siJQoqhKk:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bryanbcook/~4/DKYtLWhyqJc" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.bryancook.net/feeds/1637986703219010666/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=8942599&amp;postID=1637986703219010666" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8942599/posts/default/1637986703219010666?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8942599/posts/default/1637986703219010666?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/bryanbcook/~3/DKYtLWhyqJc/twelve-days-of-code-windows-7-shell.html" title="Twelve Days of Code – Windows 7 Shell Integration" /><author><name>bryan</name><uri>http://www.blogger.com/profile/01332614158223702009</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="09074999001888279291" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.bryancook.net/2009/12/twelve-days-of-code-windows-7-shell.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0YNRHgzcSp7ImA9WxBREUk.&quot;"><id>tag:blogger.com,1999:blog-8942599.post-3261312276534529901</id><published>2009-12-29T23:59:00.001-05:00</published><updated>2009-12-29T23:59:55.689-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-12-29T23:59:55.689-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term=".net" /><category scheme="http://www.blogger.com/atom/ns#" term="twelve-days-of-code" /><title>Twelve Days of Code – Views</title><content type="html">&lt;p&gt;As part of the &lt;a href="http://www.bryancook.net/2009/12/twelve-days-of-code-challenge-2009.html"&gt;twelve days of code&lt;/a&gt;, I’m building a Pomodoro style task tracking application and blogging about it. This post is the fourth in this series -- we’ll look at some basic XAML for the View, data binding and some simple styling.&lt;/p&gt;  &lt;h3&gt;Some basic Layout&lt;/h3&gt;  &lt;p&gt;Our Pomodoro application doesn’t require a sophisticated layout with complex XAML.&amp;#160; All we really need is an area for our count down timer, and two buttons to start and cancel the Pomodoro.&amp;#160; That being said, we’ll drop a textbox and two buttons into a grid, like so:&lt;/p&gt;  &lt;pre class="brush: xml;"&gt;&amp;lt;Window x:Class=&amp;quot;Pomodoro.Shell.MainWindow&amp;quot;
        xmlns=&amp;quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&amp;quot;
        xmlns:x=&amp;quot;http://schemas.microsoft.com/winfx/2006/xaml&amp;quot;
        Title=&amp;quot;Pomodoro&amp;quot; Width=&amp;quot;250&amp;quot; Height=&amp;quot;120&amp;quot;&amp;gt;
    
    &amp;lt;Grid&amp;gt;
        &amp;lt;Grid.RowDefinitions&amp;gt;
            &amp;lt;RowDefinition /&amp;gt;
        &amp;lt;/Grid.RowDefinitions&amp;gt;
        &amp;lt;Grid.ColumnDefinitions&amp;gt;
            &amp;lt;ColumnDefinition /&amp;gt;
            &amp;lt;ColumnDefinition /&amp;gt;
        &amp;lt;/Grid.ColumnDefinitions&amp;gt;
        
        &amp;lt;!-- active item --&amp;gt;
        &amp;lt;TextBlock Grid.Column=&amp;quot;0&amp;quot;/&amp;gt;
        
        &amp;lt;!-- command buttons --&amp;gt;
        &amp;lt;StackPanel Orientation=&amp;quot;Horizontal&amp;quot; Grid.Column=&amp;quot;1&amp;quot;&amp;gt;
            &amp;lt;Button Content=&amp;quot;Start&amp;quot; /&amp;gt;
            &amp;lt;Button Content=&amp;quot;Stop&amp;quot; /&amp;gt;
        &amp;lt;/StackPanel&amp;gt;
        
    &amp;lt;/Grid&amp;gt;
&amp;lt;/Window&amp;gt;&lt;/pre&gt;

&lt;p&gt;Which looks something likes this:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://lh4.ggpht.com/_XB6P2gWz--8/SzreRW8U1pI/AAAAAAAAAdA/--Vlu_AcG9w/s1600-h/BasicPomodoroApp2.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="Basic Pomodo - Yuck!" border="0" alt="Basic Pomodo - Yuck!" src="http://lh4.ggpht.com/_XB6P2gWz--8/SzreR1GuwuI/AAAAAAAAAdE/ROJl6u3c7K0/BasicPomodoroApp_thumb.png?imgmax=800" width="244" height="119" /&gt;&lt;/a&gt; &lt;/p&gt;

&lt;h3&gt;Binding to the ViewModel&lt;/h3&gt;

&lt;p&gt;The great thing behind the Model-View-ViewModel pattern is that we don’t need goofy “code-behind” logic to control the View.&amp;#160; Instead, we use WPF’s powerful data binding to graph the View to the ViewModel.&amp;#160; &lt;/p&gt;

&lt;p&gt;There are many different ways to bind the ViewModel to the View, but here’s a quick and dirty mechanism until there’s a inversion of control container to resolve the ViewModel:&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        var sessionController = new TaskSessionController();
        var alarmController = new TaskAlarmController();

        var ctx = new TaskApplicationViewModel(sessionController, alarmController);
        this.DataContext = ctx;
    }
}&lt;/pre&gt;

&lt;p&gt;The XAML binds to the TaskApplicationViewModel’s ActiveItem, StartCommand and CancelCommands:&lt;/p&gt;

&lt;pre class="brush: xml;"&gt;&amp;lt;!-- active item --&amp;gt;
&amp;lt;TextBlock Text=&amp;quot;{Binding ActiveItem.TimeRemaining}&amp;quot; /&amp;gt;

&amp;lt;!-- command buttons --&amp;gt;
&amp;lt;StackPanel Orientation=&amp;quot;Horizontal&amp;quot; Grid.Column=&amp;quot;1&amp;quot;&amp;gt;
    &amp;lt;Button Content=&amp;quot;Start&amp;quot; 
            Command=&amp;quot;{Binding StartCommand}&amp;quot; 
            CommandParameter=&amp;quot;{Binding ActiveItem}&amp;quot;                 
            /&amp;gt;
    &amp;lt;Button Content=&amp;quot;Stop&amp;quot; 
            Command=&amp;quot;{Binding CancelCommand}&amp;quot; 
            CommandParameter=&amp;quot;{Binding ActiveItem}&amp;quot;
            /&amp;gt;
&amp;lt;/StackPanel&amp;gt;&lt;/pre&gt;

&lt;h3&gt;The Count Down Timer&lt;/h3&gt;

&lt;p&gt;At this point, clicking on the Start button shows the pomodoro counting down in milliseconds.&amp;#160; I had considered breaking the count down timer into its own user control, but chose to be pragmatic and use a simple TextBlock.&amp;#160; The display can be dressed up using a simple converter:&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;namespace Pomodoro.Shell.Converters
{
    [ValueConversion(typeof(int), typeof(string))]
    public class TimeRemainingConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if (value is int)
            {
                int remaining = (int)value;

                TimeSpan span = TimeSpan.FromMilliseconds(remaining);

                return String.Format(&amp;quot;{0:00}:{1:00}&amp;quot;, span.Minutes, span.Seconds);
            }
            return String.Empty;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}&lt;/pre&gt;

&lt;p&gt;&lt;/p&gt;

&lt;h3&gt;Control Button Visibility&lt;/h3&gt;

&lt;p&gt;With the data binding so far, the Pomodoro application comes to life with a crude but functional user-interface.&amp;#160; To improve the experience, I’d like to make the buttons contextual so that only the appropriate button is shown based on the state of the session.&amp;#160; To achieve this effect, we bind to the &lt;em&gt;IsActive&lt;/em&gt; property to control visibility state with mutually exclusive converters: &lt;em&gt;BooleanToVisibleConverter&lt;/em&gt; and &lt;em&gt;BooleanToCollapsedConverter&lt;/em&gt;.&amp;#160; I honestly don’t know why these converters aren’t part of the framework as I use this concept frequently.&amp;#160; &lt;/p&gt;

&lt;pre class="brush: csharp; collapse: true;"&gt;namespace Pomodoro.Shell.Converters
{
    [ValueConversion(typeof(bool), typeof(Visibility))]
    public class BooleanToHiddenConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if (value is bool)
            {
                bool hidden = (bool)value;
                if (hidden)
                {
                    return Visibility.Collapsed;
                }
            }
            return Visibility.Visible;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

    [ValueConversion(typeof(bool),typeof(Visibility))]
    public class BooleanToVisibleConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if (value is bool)
            {
                bool active = (bool)value;

                if (active)
                {
                    return Visibility.Visible;
                }
            }
            return Visibility.Collapsed;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}&lt;/pre&gt;

&lt;p&gt;However, there’s a Binding problem with all of these converters: by default, the ActiveItem of the TaskApplication is null and none of the bindings will take effect until after the object has been set.&amp;#160; This is easily fixed with a &lt;em&gt;FallbackValue&lt;/em&gt; in the binding syntax:&lt;/p&gt;

&lt;pre class="brush: csharp; highlight: [15,23,29];"&gt;&amp;lt;Window ...
        xmlns:local=&amp;quot;clr-namespace:Pomodoro.Shell.Converters&amp;quot;
        xmlns:pc=&amp;quot;clr-namespace:System.Windows;assembly=PresentationCore&amp;quot;&amp;gt;

    &amp;lt;Window.Resources&amp;gt;
        &amp;lt;local:BooleanToVisibleConverter x:Key=&amp;quot;BoolToVisibleConverter&amp;quot; /&amp;gt;
        &amp;lt;local:BooleanToHiddenConverter x:Key=&amp;quot;BoolToHiddenConverter&amp;quot; /&amp;gt;
        &amp;lt;local:TimeRemainingConverter x:Key=&amp;quot;TimeSpanConverter&amp;quot; /&amp;gt;
    &amp;lt;/Window.Resources&amp;gt;

    &amp;lt;Grid&amp;gt;

        &amp;lt;!-- active item --&amp;gt;
        &amp;lt;TextBlock Text=&amp;quot;{Binding ActiveItem.TimeRemaining, Converter={StaticResource TimeSpanConverter}, 
                                  FallbackValue='00:00'}&amp;quot; /&amp;gt;

        &amp;lt;!-- command buttons --&amp;gt;
        &amp;lt;StackPanel Orientation=&amp;quot;Horizontal&amp;quot; Grid.Column=&amp;quot;1&amp;quot;&amp;gt;
            &amp;lt;Button Content=&amp;quot;Start&amp;quot; 
                    Command=&amp;quot;{Binding StartCommand}&amp;quot; 
                    CommandParameter=&amp;quot;{Binding ActiveItem}&amp;quot;
                    Visibility=&amp;quot;{Binding ActiveItem.IsActive, Converter={StaticResource BoolToHiddenConverter}, 
                    FallbackValue={x:Static Member=pc:Visibility.Visible}}&amp;quot;                   
                    /&amp;gt;
            &amp;lt;Button Content=&amp;quot;Stop&amp;quot; 
                    Command=&amp;quot;{Binding CancelCommand}&amp;quot; 
                    CommandParameter=&amp;quot;{Binding ActiveItem}&amp;quot;
                    Visibility=&amp;quot;{Binding ActiveItem.IsActive, Converter={StaticResource BoolToVisibleConverter}, 
                    FallbackValue={x:Static Member=pc:Visibility.Collapsed}}&amp;quot;
                    /&amp;gt;
        &amp;lt;/StackPanel&amp;gt;

    &amp;lt;/Grid&amp;gt;
&amp;lt;/Window&amp;gt;&lt;/pre&gt;

&lt;p&gt;A few extra applied stylings, and the Pomodoro app is shaping up nicely:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://lh6.ggpht.com/_XB6P2gWz--8/SzreSG__o3I/AAAAAAAAAdI/NgNQ7lxOaWU/s1600-h/Final-ish-Pomodoro%5B2%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="Not so bad Pomodoro" border="0" alt="Not so bad Pomodoro" src="http://lh6.ggpht.com/_XB6P2gWz--8/SzreSmUdK2I/AAAAAAAAAdM/liMAhOOUAjg/Final-ish-Pomodoro_thumb.png?imgmax=800" width="186" height="72" /&gt;&lt;/a&gt; &lt;/p&gt;

&lt;h3&gt;Next Steps&lt;/h3&gt;

&lt;p&gt;The next post, I’ll look at some of the new Windows 7 integration features available in WPF 4.0.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8942599-3261312276534529901?l=www.bryancook.net' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/KGZD9MQV4nBGLXq0xSJKh8p50Cw/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/KGZD9MQV4nBGLXq0xSJKh8p50Cw/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/KGZD9MQV4nBGLXq0xSJKh8p50Cw/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/KGZD9MQV4nBGLXq0xSJKh8p50Cw/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=X1FpFd6yUF0:UZAvVv0-TDU:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=X1FpFd6yUF0:UZAvVv0-TDU:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=X1FpFd6yUF0:UZAvVv0-TDU:G79ilh31hkQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=G79ilh31hkQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=X1FpFd6yUF0:UZAvVv0-TDU:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?i=X1FpFd6yUF0:UZAvVv0-TDU:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bryanbcook/~4/X1FpFd6yUF0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.bryancook.net/feeds/3261312276534529901/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=8942599&amp;postID=3261312276534529901" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8942599/posts/default/3261312276534529901?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8942599/posts/default/3261312276534529901?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/bryanbcook/~3/X1FpFd6yUF0/twelve-days-of-code-views.html" title="Twelve Days of Code – Views" /><author><name>bryan</name><uri>http://www.blogger.com/profile/01332614158223702009</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="09074999001888279291" /></author><thr:total>1</thr:total><feedburner:origLink>http://www.bryancook.net/2009/12/twelve-days-of-code-views.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Ak4NR386fSp7ImA9WxBWFEU.&quot;"><id>tag:blogger.com,1999:blog-8942599.post-254342678094031786</id><published>2009-12-24T15:51:00.002-05:00</published><updated>2010-02-06T15:43:16.115-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-02-06T15:43:16.115-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term=".net" /><category scheme="http://www.blogger.com/atom/ns#" term="twelve-days-of-code" /><title>Twelve Days of Code – Pomodoro Object Model</title><content type="html">&lt;p&gt;As part of the &lt;a href="http://www.bryancook.net/2009/12/twelve-days-of-code-challenge-2009.html" target="_blank"&gt;twelve days of code&lt;/a&gt;, I’m building a Pomodoro style task tracking application and blogging about it.  You can play along at home too.  This post talks about the process of defining the object model and parts of the presentation model.&lt;/p&gt;  &lt;p&gt;Most of the &lt;em&gt;Model-View-ViewModel&lt;/em&gt; examples I’ve seen have not put emphasis on the design of the Model, so I’m not sure that I can call what I’m doing proper MVVM, but i like to have a fully functional application independent of its presentation and then extend parts of the model into presentation model objects.  As I was designing the object model, I chose to use interfaces rather than concrete classes.  There are several reasons for this, but the main factor was to apply a top-down Test-Driven-Development methodology.&lt;/p&gt;  &lt;h3&gt;On Top-Down Test-Driven-Development…&lt;/h3&gt;  &lt;p&gt;The theory behind “top-down TDD” is that by using the Application or other top-level object as the starting point, I only have to work with that object and its immediate dependencies rather than the entire implementation.  As dependencies for that object are realized, I create a simple interface rather than a fully flushed out implementation -- I find using interfaces really helps to visualize the code’s responsibilities.  Using interfaces also makes the design more pliable, in that I can move responsibilities around without having to overhaul the implementation.&lt;/p&gt;  &lt;p&gt;This approach plays well into test-driven-development since tools like RhinoMocks and Moq can quickly generate mock implementations for interfaces at runtime.  These dynamic implementations make it possible to define how dependencies will operate under normal (and irregular) circumstances which allows me to concentrate on the object I’m currently constructing rather than several objects at once.  After writing a few small tests, I make a conscious effort to validate or rework the object model.&lt;/p&gt;  &lt;p&gt;Using the Context/Specification strategy I outlined &lt;a href="http://www.bryancook.net/2009/10/my-current-approach-to.html" target="_blank"&gt;a few posts ago&lt;/a&gt;, the process I followed resembled something like this:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Design a simple interface (one or two simple methods) &lt;/li&gt;    &lt;li&gt;Create the concrete class and the test fixture &lt;/li&gt;    &lt;li&gt;After spending a few minutes thinking about the different scenarios or states this object could represent, I create a test class for that context &lt;/li&gt;    &lt;li&gt;Write a specification (test) for that context &lt;/li&gt;    &lt;li&gt;Write the assertion for that specification &lt;/li&gt;    &lt;li&gt;Refine the context / subject-initialization / because &lt;/li&gt;    &lt;li&gt;Write the implementation until the test passes &lt;/li&gt;    &lt;li&gt;Refactor. &lt;/li&gt;    &lt;li&gt;Add more tests. &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;(Confession: I started writing the ITaskApplication first but then switched to ITaskSessionController because I had a clearer vision of what it needed to do.)  More on tests later, first let’s look more at the object model.&lt;/p&gt;  &lt;h3&gt;The Pomodoro Object Model&lt;/h3&gt;  &lt;p&gt;Here’s a quick diagram of the object model as seen by its interfaces.  Note that instead of referring to “Pomodoro” or “Tomato”, I’m using a more generic term “Task”:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_XB6P2gWz--8/SzPUOm9h0NI/AAAAAAAAAcw/XgfLM0Zb7MA/s1600-h/Pomodor.Core7.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="Pomodor.Core" border="0" alt="Pomodor.Core" src="http://lh3.ggpht.com/_XB6P2gWz--8/SzPUPU4OzeI/AAAAAAAAAc0/6nci2RnTDVg/Pomodor.Core_thumb5.png?imgmax=800" width="512" height="484" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;In the above diagram, the Task Application represents the top of the application where the user will interact to start and stop (cancel) a task session.  By design, the application seats one session at a time and it delegates the responsibility for constructing and initializing a task session to the Session Controller.  The Task Application is also responsible for monitoring the session’s &lt;em&gt;SessionFinished&lt;/em&gt; event, which when fired, the Application delegates to the &lt;em&gt;AlarmController&lt;/em&gt; and &lt;em&gt;SessionController&lt;/em&gt; to notify the user and track completion of the session.&lt;/p&gt;  &lt;p&gt;Presently the &lt;em&gt;TaskController&lt;/em&gt; acts primarily as a factory for new sessions and has no immediate dependencies, though it would logically communicate with a persistence store to track usage information (when I get around to that).&lt;/p&gt;  &lt;p&gt;The &lt;em&gt;TaskSession&lt;/em&gt; object encapsulates the internal state of the session as well as the timer logic.  When the timer completes, the &lt;em&gt;SessionFinished&lt;/em&gt; event is fired.&lt;/p&gt;  &lt;h3&gt;The Realized Implementation&lt;/h3&gt;  &lt;p&gt;Using a TDD methodology, I created concrete implementations for each interface as I went.  At this point, the division between Presentation Model (aka &lt;em&gt;ViewModel&lt;/em&gt;) and Controller Logic (aka &lt;em&gt;Model&lt;/em&gt;) are clearly visible:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_XB6P2gWz--8/SzkZ5leSMSI/AAAAAAAAAc4/kBlTwXs_Zrs/s1600-h/Pomodoro.Core.Implementation%5B3%5D.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="Pomodoro.Core.Implementation" border="0" alt="Pomodoro.Core.Implementation" src="http://lh6.ggpht.com/_XB6P2gWz--8/SzkZ6AP4-OI/AAAAAAAAAc8/1_GyX_QHKR8/Pomodoro.Core.Implementation_thumb%5B1%5D.png?imgmax=800" width="644" height="434" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;To help realize the implementation, I borrowed a few key classes from the internets:&lt;/p&gt;  &lt;h4&gt;BaseViewModel&lt;/h4&gt;  &lt;p&gt;When starting out with the Presentation Model, I found this &lt;a href="http://msdn.microsoft.com/en-us/magazine/dd419663.aspx" target="_blank"&gt;article as a good background and starting point&lt;/a&gt; for the Model-View-ViewModel pattern.  The article provides a &lt;em&gt;ViewModelBase&lt;/em&gt; implementation which contains the common base ViewModel plumbing, including the &lt;em&gt;IDisposable&lt;/em&gt; pattern.  The most notable part is the implementation for &lt;em&gt;INotifyPropertyChanged&lt;/em&gt; which contains debug only validation logic that throws an error if the ViewModel attempts to raise a PropertyChanged event for a property that does not exist.  This simple trick does away with stupid bugs related to ViewModel binding errors caused by typos.&lt;/p&gt;  &lt;p&gt;I’ve also added &lt;em&gt;BeginInitialization&lt;/em&gt; and &lt;em&gt;EndInitialization&lt;/em&gt; members which prevent the &lt;em&gt;PropertyChanged&lt;/em&gt; event from firing during initialization.  This trick comes in handy when the ViewModel is sufficiently complicated enough that raising the event needlessly impacts performance.&lt;/p&gt;  &lt;p&gt;For some reason, I prefer the name &lt;em&gt;BaseViewModel&lt;/em&gt; over &lt;em&gt;ViewModelBase&lt;/em&gt;.  How about you?&lt;/p&gt;  &lt;h4&gt;DelegateCommand&lt;/h4&gt;  &lt;p&gt;When it came time to add Commands to my ViewModels, I considered brining &lt;a href="http://www.codeplex.com/CompositeWPF/" target="_blank"&gt;Prism&lt;/a&gt; to my application primarily to get the &lt;em&gt;DelegateCommand&lt;/em&gt; implementation.  Perhaps I missed something, but I was only able to find the &lt;a href="http://msdn.microsoft.com/en-us/library/ee712881.aspx" target="_blank"&gt;source code for download&lt;/a&gt;.  Ultimately, I chose to take the &lt;em&gt;DelegateCommand&lt;/em&gt; code-file and include it as part of the Core library instead of compiling the libraries for Prism.  The decision to not compile the libraries was based on some tweaking and missing references for .NET 4.0, and the projects were not configured to compile against a strong-name which I would need for my strongly-named application.&lt;/p&gt;  &lt;p&gt;The DelegateCommand provides an implementation of the &lt;em&gt;ICommand&lt;/em&gt; interface that accepts delegates for the &lt;em&gt;Execute&lt;/em&gt; and &lt;em&gt;CanExecute&lt;/em&gt; methods, as well as a hook to raise the &lt;em&gt;CanExecuteChanged&lt;/em&gt; event.  At some point, I may choose to pull the implementation for the Start and Cancel commands out of the TaskApplicationViewModel and into separate classes.&lt;/p&gt;  &lt;h3&gt;More About the Tests&lt;/h3&gt;  &lt;p&gt;As promised, here is some more information about the tests and methodology.  Rather than list the code for the tests, I thought it would be fun to list the names of the tests that were created during this stage of the development process.  Because I’m using a context/specification test-style, my tests should read like specifications:&lt;/p&gt;  &lt;p&gt;&lt;b&gt;TaskApplicationSpecs&lt;/b&gt;: &lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;when_a_task_completes      &lt;ul&gt;       &lt;li&gt;should_display_an_alarm &lt;/li&gt;        &lt;li&gt;should_record_completed_task &lt;/li&gt;     &lt;/ul&gt;   &lt;/li&gt;    &lt;li&gt;when_a_task_is_cancelled      &lt;ul&gt;       &lt;li&gt;should record cancellation &lt;/li&gt;     &lt;/ul&gt;   &lt;/li&gt;    &lt;li&gt;when_a_task_is_started      &lt;ul&gt;       &lt;li&gt;should_create_a_new_session &lt;/li&gt;        &lt;li&gt;ensure_new_tasks_cannot_be_started &lt;/li&gt;        &lt;li&gt;ensure_running_task_can_be_cancelled &lt;/li&gt;     &lt;/ul&gt;   &lt;/li&gt;    &lt;li&gt;when_no_task_is_running      &lt;ul&gt;       &lt;li&gt;ensure_new_task_can_be_started &lt;/li&gt;        &lt;li&gt;ensure_cancel_task_command_is_disabled &lt;/li&gt;     &lt;/ul&gt;   &lt;/li&gt; &lt;/ul&gt; &lt;b&gt;TaskSessionControllerSpecs&lt;/b&gt;:   &lt;ul&gt;   &lt;li&gt;when_cancelling_a_tasksession      &lt;ul&gt;       &lt;li&gt;ensure_session_is_stopped &lt;/li&gt;     &lt;/ul&gt;   &lt;/li&gt;    &lt;li&gt;when_creating_a_tasksession      &lt;ul&gt;       &lt;li&gt;should_create_a_task_with_a_valid_identifier &lt;/li&gt;        &lt;li&gt;ensure_start_date_is_set &lt;/li&gt;        &lt;li&gt;ensure_session_is_started &lt;/li&gt;        &lt;li&gt;ensure_session_length_is_set &lt;/li&gt;     &lt;/ul&gt;   &lt;/li&gt;    &lt;li&gt;when_finishing_a_tasksession      &lt;ul&gt;       &lt;li&gt;ensure_session_is_stopped &lt;/li&gt;     &lt;/ul&gt;   &lt;/li&gt; &lt;/ul&gt; &lt;b&gt;TaskSessionViewModelspecs&lt;/b&gt;:   &lt;ul&gt;   &lt;li&gt;given_a_default_session      &lt;ul&gt;       &lt;li&gt;should_be_disabled &lt;/li&gt;        &lt;li&gt;should_not_have_start_date_set &lt;/li&gt;        &lt;li&gt;should_not_have_end_date_set &lt;/li&gt;        &lt;li&gt;should_have_valid_id &lt;/li&gt;     &lt;/ul&gt;   &lt;/li&gt;    &lt;li&gt;when_session_is_ended      &lt;ul&gt;       &lt;li&gt;ensure_endtime_is_recorded &lt;/li&gt;     &lt;/ul&gt;   &lt;/li&gt;    &lt;li&gt;when_session_is_started      &lt;ul&gt;       &lt;li&gt;should_be_active &lt;/li&gt;        &lt;li&gt;should_have_initial_time_interval_set &lt;/li&gt;        &lt;li&gt;should_have_less_time_remaining_than_original_session_length &lt;/li&gt;        &lt;li&gt;should_notify_the_ui_to_update_the_counter_periodically &lt;/li&gt;        &lt;li&gt;should_record_start_time_of_task &lt;/li&gt;        &lt;li&gt;ensure_end_time_of_task_has_not_been_recorded &lt;/li&gt;     &lt;/ul&gt;   &lt;/li&gt;    &lt;li&gt;when_timer_completes      &lt;ul&gt;       &lt;li&gt;should_raise_finish_event &lt;/li&gt;        &lt;li&gt;ensure_finish_event_is_only_raised_once &lt;/li&gt;        &lt;li&gt;should_run_the_counter_down_to_zero &lt;/li&gt;        &lt;li&gt;should_disable_task &lt;/li&gt;        &lt;li&gt;ensure_endtime_has_not_been_recorded &lt;/li&gt;     &lt;/ul&gt;   &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;&lt;/p&gt;  &lt;h3&gt;Next Steps&lt;/h3&gt;  &lt;p&gt;The next logical step in the twelve days of code is creating the &lt;em&gt;View&lt;/em&gt; and wiring it up to the &lt;em&gt;ViewModel.&lt;/em&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8942599-254342678094031786?l=www.bryancook.net' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/0kb2GDXRSJpCgsUl0gpe07rEBaM/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/0kb2GDXRSJpCgsUl0gpe07rEBaM/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/0kb2GDXRSJpCgsUl0gpe07rEBaM/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/0kb2GDXRSJpCgsUl0gpe07rEBaM/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=bLjt6MgK9Sc:QaLv28v5ZDg:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=bLjt6MgK9Sc:QaLv28v5ZDg:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=bLjt6MgK9Sc:QaLv28v5ZDg:G79ilh31hkQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=G79ilh31hkQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=bLjt6MgK9Sc:QaLv28v5ZDg:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?i=bLjt6MgK9Sc:QaLv28v5ZDg:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bryanbcook/~4/bLjt6MgK9Sc" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.bryancook.net/feeds/254342678094031786/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=8942599&amp;postID=254342678094031786" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8942599/posts/default/254342678094031786?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8942599/posts/default/254342678094031786?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/bryanbcook/~3/bLjt6MgK9Sc/twelve-days-of-code-pomodoro-object.html" title="Twelve Days of Code – Pomodoro Object Model" /><author><name>bryan</name><uri>http://www.blogger.com/profile/01332614158223702009</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="09074999001888279291" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.bryancook.net/2009/12/twelve-days-of-code-pomodoro-object.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUMBQXwyfip7ImA9WxBSFkU.&quot;"><id>tag:blogger.com,1999:blog-8942599.post-144805110531389812</id><published>2009-12-24T15:43:00.001-05:00</published><updated>2009-12-24T15:44:10.296-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-12-24T15:44:10.296-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term=".net" /><category scheme="http://www.blogger.com/atom/ns#" term="twelve-days-of-code" /><category scheme="http://www.blogger.com/atom/ns#" term="Software Design" /><title>Twelve Days of Code - Solution Setup</title><content type="html">&lt;p&gt;As part of the &lt;a href="http://www.bryancook.net/2009/12/twelve-days-of-code-challenge-2009.html" target="_blank"&gt;Twelve Days of Code Challenge&lt;/a&gt;, I’m developing a Pomodoro style application and sharing the progress here on my blog.&amp;#160; This post tackles day one: setting up your project.&lt;/p&gt;  &lt;p&gt;The initial stage of a project where you are figuring out layers and packaging is a critical part of the process and one that I’ve always found interesting.&amp;#160; From past experience, the small details at this stage can become massive technical debt later if the wrong approach is used, so it’s best to take your time and make sure you’ve crossed all the T’s and dotted the I’s.&lt;/p&gt;  &lt;h3&gt;Creating the Solution&lt;/h3&gt;  &lt;p&gt;For this project I’ve chosen to use Visual Studio 2010 Beta 2 and so far the experience has been great.&amp;#160; Visual Studio 2010 is going to reset the standard and bring new levels of developer productivity (assuming they solve some of the stability issues): it’s faster and much more responsive, eats less memory and adds subtle UX refinements that improve developer flow.&amp;#160; To get a better sense and to recreate this feeling, I urge you to load up Visual Studio 2003 and look at the Start page – we’ve come a long way.&lt;/p&gt;  &lt;p&gt;The &lt;em&gt;New Project&lt;/em&gt; window has a nice overhaul, and we can specify the target framework in the dialog.&amp;#160; Here I’m creating a WPF Application &lt;em&gt;Pomodoro.Shell&lt;/em&gt;.&amp;#160; Note that I’m specifying to create a directory for the solution and that the Solution Name and Project Name are different.&amp;#160; &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_XB6P2gWz--8/SzPSO5jn1OI/AAAAAAAAAcQ/KYBf3l8wHrE/s1600-h/AddSolution3.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="AddSolution" border="0" alt="AddSolution" src="http://lh6.ggpht.com/_XB6P2gWz--8/SzPSQN6mZCI/AAAAAAAAAcU/I56tltiji1g/AddSolution_thumb1.png?imgmax=800" width="644" height="434" /&gt;&lt;/a&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;Normally at this point I would consider renaming the output of the application from “Pomodoro.Shell.exe” to some other simpler name like “pomodoro.exe”.&amp;#160; This is an optional step which I won’t bother with for this application.&lt;/p&gt;  &lt;h3&gt;Adding Projects&lt;/h3&gt;  &lt;p&gt;When laying out the solution, the first challenge is determining how many Visual Studio Projects we’ll need, and there are many factors to consider including dependencies, security, versioning, deployment, reuse, etc.&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;There appears to be a school of thought that believes every component or module should be its own assembly, and I strongly disagree.&amp;#160; Assemblies should be thought of as &lt;em&gt;deployment-units&lt;/em&gt; – if the components version and deploy together, its very likely that they should be a single assembly.&amp;#160; As Visual Studio does not handle large number of projects well, it’s always better to start with larger assemblies and separate them later if needed.&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;For my pomodoro app, I’ve decided to structure the project into two primary pieces, “core” and “shell”, where “core” provides the model of the application and “shell” provides the user-interface specific plumbing.&lt;/p&gt;  &lt;h3&gt;Add Test Projects&lt;/h3&gt;  &lt;p&gt;Right from the start of the project, I’m gearing towards how it will be tested.&amp;#160; As such, I’ve created two test projects, one for each assembly.&amp;#160; This allow me to keep the logical division between assemblies.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_XB6P2gWz--8/SzPSQ6rwH5I/AAAAAAAAAcY/FDIAFlzoXBg/s1600-h/AddProject3.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="AddProject" border="0" alt="AddProject" src="http://lh5.ggpht.com/_XB6P2gWz--8/SzPSSCY1A0I/AAAAAAAAAcc/OlbIUyN7Qz0/AddProject_thumb1.png?imgmax=800" width="644" height="434" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;As soon as the projects are created, the first thing I’ll do is adjust the namespaces of the test libraries to match their counterparts.&amp;#160; By extension, the tests are features of the same namespace but they are packaged in a separate assembly because I do not want to deploy them with the application.&amp;#160; &lt;a href="http://www.bryancook.net/2008/05/tdd-tips-unit-test-namespace.html" target="_blank"&gt;I’ve written about this before&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_XB6P2gWz--8/SzPSVahqx5I/AAAAAAAAAcg/xQBLwVXjJOg/s1600-h/FixNamespaceforTests3.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="FixNamespaceforTests" border="0" alt="FixNamespaceforTests" src="http://lh6.ggpht.com/_XB6P2gWz--8/SzPSW7BzVGI/AAAAAAAAAck/5jO0gKt8dbo/FixNamespaceforTests_thumb1.png?imgmax=800" width="643" height="484" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;h3&gt;Configure common Assembly Properties&lt;/h3&gt;  &lt;p&gt;Once we’ve settled into a project structure, the next easy win is to configure the projects to share the same the assembly details such as version number, manufacture, copyright, etc.&amp;#160; This is easily accomplished by creating a single file to represent this data, and then &lt;a href="http://msdn.microsoft.com/en-us/library/9f4t9t92.aspx" target="_blank"&gt;linking each project to this file&lt;/a&gt;.&amp;#160; At a later step, this file can be auto-generated as part of the build process.&lt;/p&gt;  &lt;pre class="brush: csharp; toolbar: false;"&gt;using System.Reflection;

[assembly: AssemblyVersion(&amp;quot;1.0.0.0&amp;quot;)]
[assembly: AssemblyFileVersion(&amp;quot;1.0.0.0&amp;quot;)]
[assembly: AssemblyCompany(&amp;quot;Bryan Cook&amp;quot;)]
[assembly: AssemblyCopyright(&amp;quot;Copyright © Bryan Cook 2009&amp;quot;)]
[assembly: AssemblyTrademark(&amp;quot;&amp;quot;)]
[assembly: AssemblyCulture(&amp;quot;&amp;quot;)]&lt;/pre&gt;

&lt;p&gt;Tip: Link the AssemblyVersion.cs file to the root of each project, then drag it into the Properties folder.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;

&lt;h3&gt;Give the Assemblies a Strong-Name&lt;/h3&gt;

&lt;p&gt;If your code will ultimately end up on a end-user desktop, it is imperative to give the assembly a strong-name.&amp;#160; We can take advantage of Visual Studio’s built in features to create our strong-name-key (snk) but we’ll also take a few extra steps to ensure that each project has the same key.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Open the project properties. &lt;/li&gt;

  &lt;li&gt;Click on the Signing tab &lt;/li&gt;

  &lt;li&gt;Check the “Sign the assembly” checkbox &lt;/li&gt;

  &lt;li&gt;Choose “&amp;lt;New…&amp;gt;” &lt;/li&gt;

  &lt;li&gt;Create a key with no password. &lt;/li&gt;

  &lt;li&gt;Open Windows Explorer and copy the &lt;em&gt;snk&lt;/em&gt; file to the root of the solution. &lt;/li&gt;

  &lt;li&gt;Then for each project: 
    &lt;ol&gt;
      &lt;li&gt;Check the “Sign the assembly” checkbox &lt;/li&gt;

      &lt;li&gt;Choose “&amp;lt;Browse…&amp;quot;&amp;gt;” &lt;/li&gt;

      &lt;li&gt;Navigate to the root of the solution and select the &lt;em&gt;snk&lt;/em&gt; key. &lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Note that Visual Studio will copy the &lt;em&gt;snk&lt;/em&gt; file to each project folder, though each project will have the same public key.&lt;/p&gt;

&lt;h3&gt;Designate Friend Assemblies&lt;/h3&gt;

&lt;p&gt;In order to aid testing, we can configure our Shell and Core assemblies to implicitly trust our test assemblies.&amp;#160; I’ve &lt;a href="http://www.bryancook.net/2008/05/tdd-tips-internalsvisibleto-keep-your.html" target="_blank"&gt;written about the benefits before&lt;/a&gt;, but the main advantage is that I don’t have to alter type visibility for testing purposes.&amp;#160; Since the assemblies have a strong name, the &lt;em&gt;InternalsVisibleTo&lt;/em&gt; attribute requires the fully public key.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://lh4.ggpht.com/_XB6P2gWz--8/SzPSX4dFMzI/AAAAAAAAAco/umYWygJ6J0c/s1600-h/strongnameinternalsvisibleto3.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="strong-name-internalsvisibleto" border="0" alt="strong-name-internalsvisibleto" src="http://lh6.ggpht.com/_XB6P2gWz--8/SzPSZegdqkI/AAAAAAAAAcs/iS0BK97fV7E/strongnameinternalsvisibleto_thumb1.png?imgmax=800" width="644" height="421" /&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Since all the projects share the same key file, this public token will work for all the projects.&amp;#160; The following shows the &lt;em&gt;InternalsVisibleTo&lt;/em&gt; attribute for the &lt;em&gt;Pomodoro.Core&lt;/em&gt; project:&lt;/p&gt;

&lt;pre class="brush: csharp; toolbar: false;"&gt;[assembly: InternalsVisibleTo(&amp;quot;Pomodoro.Core.Tests, PublicKey=&amp;quot; +
&amp;quot;0024000004800000940000000602000000240000525341310004000001000&amp;quot; +
&amp;quot;1003be2b1a7e08d5e14167209fc318c9c16fa5d448fb48fe1f3e22a075787&amp;quot; +
&amp;quot;55b4b1cf4059185d2bd80cc5735142927fbbd3ab6eeebe6ac6af774d5fe65&amp;quot; +
&amp;quot;0a226b87ee9778cb2f6517382102894dc6d62d5a0aaa84e4403828112167a&amp;quot; +
&amp;quot;1012d5b905a37352290e4aa23f987ff2be3ccda3e27a7f7105cf5b05c0baf&amp;quot; +
&amp;quot;3ecbfd2371c0fa0&amp;quot;)]&lt;/pre&gt;

&lt;p&gt;&lt;/p&gt;

&lt;h3&gt;Setup External References&lt;/h3&gt;

&lt;p&gt;I like to put all the third-party assemblies that are referenced into the project into a “lib” folder at the root of the solution.&amp;#160; At the moment, I’m only referencing &lt;a href="http://code.google.com/p/moq/" target="_blank"&gt;Moq&lt;/a&gt; for testing purposes.&lt;/p&gt;

&lt;p&gt;A note on external references and source control: Team Foundation Server typically only pulls dependencies that are listed directly in the solution file.&amp;#160; While there are a few hacks for this (add each assembly as an existing item in a Solution Folder; or create a class library that contains the assemblies as content), I like to have all my dependencies in a separate folder with no direct association to the Visual Studio solution.&amp;#160; As a result, these references must be manually updated by performing a “&lt;em&gt;Get Latest&lt;/em&gt;” from the TFS Source Control Explorer.&amp;#160; If you’ve got a solution for this – spill it, let’s hear your thoughts.&lt;/p&gt;

&lt;h3&gt;Setup Third-Party Tools&lt;/h3&gt;

&lt;p&gt;For all third-party tools that are used as part of the build, I like to include these in a “tools” or “etc” folder at the root of the solution.&amp;#160; This approach allows me to bundle all the necessary tools for other developers to allow faster ramp-up.&amp;#160; It adds a bit of overhead when checking things out, but certainly simplifies the build script.&lt;/p&gt;

&lt;h3&gt;Setup Build Script&lt;/h3&gt;

&lt;p&gt;There’s a few extra steps I had to take to get my .NET 4.0 project to compile using NAnt.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Download the nightly build of the nant 0.86 beta1.&amp;#160; The nightly build solves the missing SdkInstallRoot build error.&lt;/li&gt;

  &lt;li&gt;Paige Cook (no relation) has a &lt;a href="http://paigecsharp.blogspot.com/2009/08/nant-net-framework-40-configuration.html" target="_blank"&gt;comprehensive configuration change&lt;/a&gt; that needs to be applied to &lt;em&gt;nant.exe.config&lt;/em&gt;.&amp;#160; &lt;/li&gt;

  &lt;li&gt;Modify Paige’s version numbers from .NET 4.0 beta 1 to beta 2.&amp;#160; (Replace all references of “&lt;em&gt;v4.0.20506”&lt;/em&gt; to “&lt;em&gt;v4.0.21006”&lt;/em&gt;)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here's a few points of interest for the build file listed below:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;I’ve defined a default target “main”.&amp;#160; This allows me to simply execute “nant” in the root solution of the folder and it’ll take care of the rest. &lt;/li&gt;

  &lt;li&gt;The “main” target is solely empty because the real work is the order of the dependencies.&amp;#160; Currently, I’m only specifying “build”, but normally I would specify “clean, build, test”. &lt;/li&gt;
&lt;/ul&gt;

&lt;pre class="brush: xml; collapse: true;"&gt;&amp;lt;project default=&amp;quot;main&amp;quot;&amp;gt;

  &amp;lt;!-- VERSION NUMBER (increment before release) --&amp;gt;
  &amp;lt;property name=&amp;quot;version&amp;quot; value=&amp;quot;1.0.0.0&amp;quot; /&amp;gt;
  
  &amp;lt;!-- SOLUTION SETTINGS --&amp;gt;
  &amp;lt;property name=&amp;quot;framework.dir&amp;quot; value=&amp;quot;${framework::get-framework-directory(framework::get-target-framework())}&amp;quot; /&amp;gt;
  &amp;lt;property name=&amp;quot;msbuild&amp;quot; value=&amp;quot;${framework.dir}\msbuild.exe&amp;quot; /&amp;gt;
  &amp;lt;property name=&amp;quot;vs.sln&amp;quot; value=&amp;quot;TwelveDays.sln&amp;quot; /&amp;gt;
  &amp;lt;property name=&amp;quot;vs.config&amp;quot; value=&amp;quot;Debug&amp;quot; /&amp;gt;

  &amp;lt;!-- FOLDERS AND TOOLS --&amp;gt;
  &amp;lt;!-- Add aliases for tools here --&amp;gt;
  
  
  &amp;lt;!-- main --&amp;gt;
  &amp;lt;target name=&amp;quot;main&amp;quot; depends=&amp;quot;build&amp;quot;&amp;gt;
  &amp;lt;/target&amp;gt;

  &amp;lt;!-- build solution --&amp;gt;
  &amp;lt;target name=&amp;quot;build&amp;quot; depends=&amp;quot;version&amp;quot;&amp;gt;

    &amp;lt;!-- compile using msbuild --&amp;gt;
    &amp;lt;exec program=&amp;quot;${msbuild}&amp;quot;
      commandline=&amp;quot;${vs.sln} /m /t:Clean;Rebuild /p:Configuration=${vs.config}&amp;quot;
      workingdir=&amp;quot;.&amp;quot;
          /&amp;gt;

  &amp;lt;/target&amp;gt;
    
  &amp;lt;!-- generate version number --&amp;gt;
  &amp;lt;target name=&amp;quot;version&amp;quot;&amp;gt;
    &amp;lt;attrib file=&amp;quot;AssemblyVersion.cs&amp;quot; readonly=&amp;quot;false&amp;quot; if=&amp;quot;${file::exists('AssemblyVersion.cs')}&amp;quot; /&amp;gt;
    &amp;lt;asminfo output=&amp;quot;AssemblyVersion.cs&amp;quot; language=&amp;quot;CSharp&amp;quot;&amp;gt;
      &amp;lt;imports&amp;gt;
        &amp;lt;import namespace=&amp;quot;System&amp;quot; /&amp;gt;
        &amp;lt;import namespace=&amp;quot;System.Reflection&amp;quot; /&amp;gt;
      &amp;lt;/imports&amp;gt;
      &amp;lt;attributes&amp;gt;
        &amp;lt;attribute type=&amp;quot;AssemblyVersionAttribute&amp;quot; value=&amp;quot;${version}&amp;quot; /&amp;gt;
        &amp;lt;attribute type=&amp;quot;AssemblyFileVersionAttribute&amp;quot; value=&amp;quot;${version}&amp;quot; /&amp;gt;
      &amp;lt;/attributes&amp;gt;
    &amp;lt;/asminfo&amp;gt;
  &amp;lt;/target&amp;gt;

&amp;lt;/project&amp;gt;&lt;/pre&gt;

&lt;h3&gt;Next Steps…&lt;/h3&gt;

&lt;p&gt;In the next post, we’ll look at the object model for our Pomodoro application.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8942599-144805110531389812?l=www.bryancook.net' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/qSMceLlniCoPRwiDA6Zk4LAwEkc/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/qSMceLlniCoPRwiDA6Zk4LAwEkc/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/qSMceLlniCoPRwiDA6Zk4LAwEkc/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/qSMceLlniCoPRwiDA6Zk4LAwEkc/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=6nbIgxSYK-w:__VFQayh9i4:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=6nbIgxSYK-w:__VFQayh9i4:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=6nbIgxSYK-w:__VFQayh9i4:G79ilh31hkQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=G79ilh31hkQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=6nbIgxSYK-w:__VFQayh9i4:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?i=6nbIgxSYK-w:__VFQayh9i4:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bryanbcook/~4/6nbIgxSYK-w" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.bryancook.net/feeds/144805110531389812/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=8942599&amp;postID=144805110531389812" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8942599/posts/default/144805110531389812?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8942599/posts/default/144805110531389812?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/bryanbcook/~3/6nbIgxSYK-w/twelve-days-solution-setup.html" title="Twelve Days of Code - Solution Setup" /><author><name>bryan</name><uri>http://www.blogger.com/profile/01332614158223702009</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="09074999001888279291" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.bryancook.net/2009/12/twelve-days-solution-setup.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEQCSXg9fip7ImA9WxBTGU0.&quot;"><id>tag:blogger.com,1999:blog-8942599.post-8214056602273704887</id><published>2009-12-15T14:45:00.000-05:00</published><updated>2009-12-15T14:46:08.666-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-12-15T14:46:08.666-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term=".net" /><category scheme="http://www.blogger.com/atom/ns#" term="Software Design" /><title>Unity, Dependency Injection and Service Locators</title><content type="html">&lt;p&gt;For a while now, I’ve been chewing on some thoughts about the service locators versus dependency injection, but struggled to find the right way to say it.&amp;#160; I sent an email to a colleague today that tried to describe some of the lessons learned from my last project.&amp;#160; It comes close the visceral feelings I have around this subject, but describes it well.&amp;#160; Here it is unaltered:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;i&gt;When you pull dependencies in from the Container, you’re using a Service Locator feature of Unity; When you push dependencies in via the constructor, you’re using Dependency Injection.&amp;#160; There’s much debate over which pattern to use.&amp;#160; When using dependency injection, the biggest advantage is that the relationship between objects is well known.&amp;#160; This helps us understand the responsibilities of each object better, may have better performance (if we’re resolving dependencies a lot) and it makes it easier to write tests that isolate the behavior of the subject under test.&lt;/i&gt;&lt;/p&gt;    &lt;p&gt;&lt;i&gt;&lt;/i&gt;&lt;/p&gt;    &lt;p&gt;&lt;i&gt;In a project where we’re resolving dependencies at runtime, the true nature of the dependencies between objects is obscured -- developers must read through the source to understand responsibilities.&amp;#160; Each time a change is made to resolve new dependencies from the container, the tests will fail.&amp;#160; Since locating and understanding the responsibility of the dependencies in the source is a complex task, the easy solution is to simply register the missing dependencies in the container and move on.&amp;#160; In most cases, the object that is registered is the concrete implementation which is also susceptible to the same dependency problem.&amp;#160; Ultimately, this compounds the problem until the tests themselves become obscured, vague, incomplete or overly complex.&lt;/i&gt;&lt;/p&gt;    &lt;p&gt;&lt;i&gt;&lt;/i&gt;&lt;/p&gt;    &lt;p&gt;&lt;i&gt;In contrast, if the dependencies were registered in the constructor, all dependencies are known at compile time.&amp;#160; Any changes made to the subject won’t compile until the tests are updated to reflect the new functionality.&amp;#160; Note that because the object doesn’t use the container, the tests become just like any other simple POCO test.&lt;/i&gt;&lt;/p&gt;    &lt;p&gt;&lt;i&gt;&lt;/i&gt;&lt;/p&gt;    &lt;p&gt;&lt;i&gt;This is not a silver bullet solution however.&amp;#160; There are times when resolving from the container makes sense – loaders and savers, for example – but even then, the container can be hidden inside a POCO factory. In contrast to service location, the impact of constructor injection is that it may require more work upfront to realize the dependencies, though this can be mitigated in some cases using a TDD methodology where the tests satisfy the responsibilities of the subject under test as it is written.&lt;/i&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;Comments welcome.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8942599-8214056602273704887?l=www.bryancook.net' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/6Y4v_DCfQmdac3jmSUlsxNB4vlM/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/6Y4v_DCfQmdac3jmSUlsxNB4vlM/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/6Y4v_DCfQmdac3jmSUlsxNB4vlM/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/6Y4v_DCfQmdac3jmSUlsxNB4vlM/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=0hxyc61evPI:Dr7ol3E6K-4:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=0hxyc61evPI:Dr7ol3E6K-4:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=0hxyc61evPI:Dr7ol3E6K-4:G79ilh31hkQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=G79ilh31hkQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=0hxyc61evPI:Dr7ol3E6K-4:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?i=0hxyc61evPI:Dr7ol3E6K-4:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bryanbcook/~4/0hxyc61evPI" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.bryancook.net/feeds/8214056602273704887/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=8942599&amp;postID=8214056602273704887" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8942599/posts/default/8214056602273704887?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8942599/posts/default/8214056602273704887?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/bryanbcook/~3/0hxyc61evPI/unity-dependency-injection-and-service.html" title="Unity, Dependency Injection and Service Locators" /><author><name>bryan</name><uri>http://www.blogger.com/profile/01332614158223702009</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="09074999001888279291" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.bryancook.net/2009/12/unity-dependency-injection-and-service.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0QFRHY-eCp7ImA9WxBRFEs.&quot;"><id>tag:blogger.com,1999:blog-8942599.post-4994836339674180023</id><published>2009-12-14T22:02:00.002-05:00</published><updated>2010-01-02T14:41:55.850-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-01-02T14:41:55.850-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term=".net" /><category scheme="http://www.blogger.com/atom/ns#" term="twelve-days-of-code" /><title>Twelve Days of Code Challenge – 2009</title><content type="html">&lt;p&gt;Two years ago I tried an experiment in blogging for the festive season called the "&lt;a href="http://www.bryancook.net/2007/12/twelve-days-of-code.html" target="_blank"&gt;Twelve Days of Code&lt;/a&gt;".  The concept was to challenge myself to experiment in new technologies for at least an hour a day and blog about it.  While I learned a lot about a focused topic (I chose Visual Studio Automation), the experiment didn't live up to my expectations. &lt;/p&gt;  &lt;p&gt;This year, I want to do something different.  I want to create a social experiment and challenge you!&lt;/p&gt;  &lt;h3&gt;The challenge&lt;/h3&gt;  &lt;p&gt;This year, the challenge is to write a simple &lt;a href="http://www.pomodorotechnique.com/" target="_blank"&gt;Pomodoro&lt;/a&gt; style task tracking application.  The application can be as simple or as far fetched as you want it to be, but at a minimum the application needs to:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Start, Stop and Cancel a Pomodoro &lt;/li&gt;    &lt;li&gt;Notify the user when the pomodoro is complete &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Of course, you don’t have to limit yourself to this functionality – sky’s the limit.  If you want to collect usage statistics and provide reporting capabilities, persist a custom task list using SQLite, or work in the other characteristics of the pomodoro “flow” such as the 3-5 minute break between tasks – that’s up to you.  Surprise me – &lt;em&gt;and yourself!&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;In the spirit of Twelve Days of Code, pick a technology that’s new to you and do as much as you can.  I'm planning on doing mine in .NET 4.0.&lt;/p&gt;  &lt;h3&gt;The rules&lt;/h3&gt;  &lt;ul&gt;   &lt;li&gt;You can tackle any task in any order &lt;/li&gt;    &lt;li&gt;Spend as much time as you want researching, but coding must be limited to one hour &lt;/li&gt;    &lt;li&gt;Blog about it and leave a comment here &lt;/li&gt;    &lt;li&gt;Have fun &lt;/li&gt; &lt;/ul&gt;  &lt;h3&gt;What do I win?&lt;/h3&gt;  &lt;p&gt;The are no prizes, unfortunately.  But if you are ever in downtown Toronto, there will be beers involved.&lt;/p&gt;  &lt;p&gt;Here’s an outline of my twelve days:&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;Solution Setup (build script, packaging, strong-names, etc)&lt;/li&gt;    &lt;li&gt;Object Model (application structure, commands, controllers)&lt;/li&gt;    &lt;li&gt;Presentation Model (View Models)&lt;/li&gt;    &lt;li&gt;Presentation templates (XAML, Converters, Data Templates)&lt;/li&gt;    &lt;li&gt;Composite Application setup (Prism)&lt;/li&gt;    &lt;li&gt;Dependency Injection Setup (Unity)&lt;/li&gt;    &lt;li&gt;Persistence Layer (record some stats using SQLite)&lt;/li&gt;    &lt;li&gt;Persistence Layer (Entity Framework 4.0)&lt;/li&gt;    &lt;li&gt;Animations (.NET 4.0 Visual State Manager)&lt;/li&gt;    &lt;li&gt;Windows 7 features (task bar icon overlays, action buttons, jump lists?)&lt;/li&gt;    &lt;li&gt;Installer (Wix)&lt;/li&gt;    &lt;li&gt;Functional Automation Testing&lt;/li&gt; &lt;/ol&gt;  &lt;p&gt;Good luck, Happy Coding and Happy Holidays.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8942599-4994836339674180023?l=www.bryancook.net' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/42FHzG3YGGE-otYolUboWZwLW0s/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/42FHzG3YGGE-otYolUboWZwLW0s/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/42FHzG3YGGE-otYolUboWZwLW0s/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/42FHzG3YGGE-otYolUboWZwLW0s/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=_-B2jWr_srI:XXgvUxMT5fk:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=_-B2jWr_srI:XXgvUxMT5fk:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=_-B2jWr_srI:XXgvUxMT5fk:G79ilh31hkQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=G79ilh31hkQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=_-B2jWr_srI:XXgvUxMT5fk:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?i=_-B2jWr_srI:XXgvUxMT5fk:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bryanbcook/~4/_-B2jWr_srI" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.bryancook.net/feeds/4994836339674180023/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=8942599&amp;postID=4994836339674180023" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8942599/posts/default/4994836339674180023?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8942599/posts/default/4994836339674180023?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/bryanbcook/~3/_-B2jWr_srI/twelve-days-of-code-challenge-2009.html" title="Twelve Days of Code Challenge – 2009" /><author><name>bryan</name><uri>http://www.blogger.com/profile/01332614158223702009</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="09074999001888279291" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.bryancook.net/2009/12/twelve-days-of-code-challenge-2009.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D0MHSHw9eip7ImA9WxBTFUk.&quot;"><id>tag:blogger.com,1999:blog-8942599.post-3793942017117616147</id><published>2009-12-11T09:30:00.000-05:00</published><updated>2009-12-11T10:30:39.262-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-12-11T10:30:39.262-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Tips" /><category scheme="http://www.blogger.com/atom/ns#" term=".net" /><category scheme="http://www.blogger.com/atom/ns#" term="Visual Studio" /><title>Visual Studio Keyboard Katas - II</title><content type="html">&lt;p&gt;Hopefully, if you &lt;a href="http://www.bryancook.net/2009/12/visual-studio-keyboard-katas.html" target="_blank"&gt;read the last kata&lt;/a&gt; and have been trying it out, you may have found yourself needing to use your mouse less for common activities such as opening files and reviewing build status.&amp;#160; This kata builds upon those previous techniques, adding seven more handy shortcuts and a pattern to practice them.&lt;/p&gt;  &lt;p&gt;Granted, the last Kata was a bit of white belt pattern: obvious and almost comical, but essential.&amp;#160; In &lt;em&gt;Tae Kwon Do, &lt;/em&gt;the yellow belt patterns introduce forward and backward motion, so it seems logical that the next kata introduces rapidly navigating forward and backward through code.&lt;/p&gt;  &lt;h3&gt;Today’s Shortcut Lesson&lt;/h3&gt;  &lt;p&gt;Our mnemonic for this set of shortcuts is centered around two keys in the upper-right area of the keyboard: F12 and Minus (-).&amp;#160; The basic combinations for these keys can be modified by using the SHIFT key.&lt;/p&gt;  &lt;p&gt;Also note, I’ve introduced another Tool window (CTRL + W&lt;em&gt;indow&lt;/em&gt;).&amp;#160; The Find Symbols Results is also displayed when you do a Quick Symbol search, which may help explain the “Q”.&lt;/p&gt;  &lt;table border="1" cellspacing="0" cellpadding="2" width="400"&gt;&lt;tbody&gt;     &lt;tr&gt;       &lt;td valign="top"&gt;F12&lt;/td&gt;        &lt;td valign="top"&gt;Go to Definition&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top"&gt;SHIFT + F12&lt;/td&gt;        &lt;td valign="top"&gt;Find all References&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top"&gt;CTRL + MINUS&lt;/td&gt;        &lt;td valign="top"&gt;Navigate Backward&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top"&gt;SHIFT + CTRL + MINUS&lt;/td&gt;        &lt;td valign="top"&gt;Navigate Forward&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top"&gt;CTRL + W, Q&lt;/td&gt;        &lt;td valign="top"&gt;Find Symbols Results Window&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top"&gt;CTRL + LEFT ARROW&lt;/td&gt;        &lt;td valign="top"&gt;Move to previous word&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top"&gt;CTRL + RIGHT ARROW&lt;/td&gt;        &lt;td valign="top"&gt;Move to next word&lt;/td&gt;     &lt;/tr&gt;   &lt;/tbody&gt;&lt;/table&gt;  &lt;p&gt;And as an extra Keeno bonus, an 8th shortcut:&lt;/p&gt;  &lt;table border="1" cellspacing="0" cellpadding="2" width="400"&gt;&lt;tbody&gt;     &lt;tr&gt;       &lt;td valign="top"&gt;CTRL + ALT + DOWN ARROW&lt;/td&gt;        &lt;td valign="top"&gt;Show MDI File List&lt;/td&gt;     &lt;/tr&gt;   &lt;/tbody&gt;&lt;/table&gt;  &lt;p&gt;&lt;/p&gt;  &lt;h3&gt;Keyboard Kata&lt;/h3&gt;  &lt;p&gt;Practice this kata any time you need to identify how a class is used.&lt;/p&gt; &lt;!--
&lt;style&gt;
span.answer { display: none; }&lt;/style&gt;
--&gt;  &lt;ol id="kata2answer"&gt;   &lt;li&gt;Open the Solution Explorer. &lt;span class="answer"&gt;(CTRL+W, S)&lt;/span&gt; &lt;/li&gt;    &lt;li&gt;Navigate to a file. &lt;span class="answer"&gt;(Arrow Keys / Enter)&lt;/span&gt; &lt;/li&gt;    &lt;li&gt;Select a property or variable &lt;span class="answer"&gt;(Arrow keys)&lt;/span&gt; &lt;/li&gt;    &lt;li&gt;Navigate to the Definition for this item &lt;span class="answer"&gt;(F12)&lt;/span&gt; &lt;/li&gt;    &lt;li&gt;Find all References of this Type &lt;span class="answer"&gt;(CTRL+LEFT to move the cursor from the definition to the type, then SHIFT+F12 for references)&lt;/span&gt; &lt;/li&gt;    &lt;li&gt;Open one of the references &lt;span class="answer"&gt;(Arrow Keys / Enter)&lt;/span&gt; &lt;/li&gt;    &lt;li&gt;Open the next reference &lt;span class="answer"&gt;(CTRL+W,Q / Arrow Keys / Enter)&lt;/span&gt; &lt;/li&gt;    &lt;li&gt;Open the nth reference &lt;span class="answer"&gt;(CTRL+W,Q / Arrow Keys / Enter)&lt;/span&gt; &lt;/li&gt;    &lt;li&gt;Navigate to the original starting point &lt;span class="answer"&gt;(CTRL + MINUS)&lt;/span&gt; &lt;/li&gt;    &lt;li&gt;Navigate to the 2nd reference &lt;span class="answer"&gt;(SHIFT + CTRL + MINUS)&lt;/span&gt; &lt;/li&gt;    &lt;li&gt;Navigate to any window &lt;span class="answer"&gt;(CTRL + ALT + DOWN / Arrow Keys / Enter)&lt;/span&gt; &lt;/li&gt; &lt;/ol&gt;  &lt;!--
&lt;a id="kata2answers" href="#"&gt;Show Answers&lt;/a&gt;
&lt;script type="text/javascript"&gt;
$("#kata2answers").click(function() {
    $("span.answer").toggle();
    return false;
});
&lt;/script&gt;
--&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8942599-3793942017117616147?l=www.bryancook.net' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/c4--Ir4g0sq8fWuMQbUPr3DciKA/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/c4--Ir4g0sq8fWuMQbUPr3DciKA/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/c4--Ir4g0sq8fWuMQbUPr3DciKA/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/c4--Ir4g0sq8fWuMQbUPr3DciKA/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=bkFy9cejoMI:677HUayEC3Y:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=bkFy9cejoMI:677HUayEC3Y:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=bkFy9cejoMI:677HUayEC3Y:G79ilh31hkQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=G79ilh31hkQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=bkFy9cejoMI:677HUayEC3Y:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?i=bkFy9cejoMI:677HUayEC3Y:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bryanbcook/~4/bkFy9cejoMI" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.bryancook.net/feeds/3793942017117616147/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=8942599&amp;postID=3793942017117616147" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8942599/posts/default/3793942017117616147?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8942599/posts/default/3793942017117616147?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/bryanbcook/~3/bkFy9cejoMI/visual-studio-keyboard-katas-ii.html" title="Visual Studio Keyboard Katas - II" /><author><name>bryan</name><uri>http://www.blogger.com/profile/01332614158223702009</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="09074999001888279291" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.bryancook.net/2009/12/visual-studio-keyboard-katas-ii.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkEARno4eyp7ImA9WxBTE0o.&quot;"><id>tag:blogger.com,1999:blog-8942599.post-8722177094551207846</id><published>2009-12-07T09:20:00.001-05:00</published><updated>2009-12-09T11:04:07.433-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-12-09T11:04:07.433-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Tips" /><category scheme="http://www.blogger.com/atom/ns#" term=".net" /><category scheme="http://www.blogger.com/atom/ns#" term="Visual Studio" /><title>Visual Studio Keyboard Katas</title><content type="html">&lt;p&gt;I’ve never spent much time learning keyboard shortcuts for Visual Studio – they’ve always seemed hard to remember with multiple key combinations, some commands have multiple shortcut bindings, and some keystrokes simply aren’t intuitive.&amp;#160; Recently, however, I’ve met a few &lt;a href="http://twitter.com/MarchyB" target="_blank"&gt;IDE Ninjas&lt;/a&gt; who have opened my eyes on the productivity gains to be had.&lt;/p&gt;  &lt;p&gt;The problem with learning keyboard shortcuts is that they can be a negative self-enforcing loop.&amp;#160; If the secret to learning keyboard shortcuts is using them during your day-to-day activities, the act of stopping work to look up an awkward keystroke interrupts your flow, lowers your productivity, and ultimately results in lost time.&amp;#160; Lost time and distractions puts pressure on us to stay focused and complete our work, which further discourages us from stopping to learn new techniques, including those that would ultimately speed us up.&amp;#160; &lt;em&gt;Oh, &lt;/em&gt;&lt;a href="http://en.wikipedia.org/wiki/Alanis_Morissette" target="_blank"&gt;&lt;em&gt;the irony&lt;/em&gt;&lt;/a&gt;&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;To break out that loop, we need to:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;learn a few shortcuts by associating them with some mnemonics; and then &lt;/li&gt;    &lt;li&gt;learn a few exercises that we can inject into daily coding flow &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;As an homage to the &lt;a href="http://codekata.pragprog.com/" target="_blank"&gt;Code Katas&lt;/a&gt; &lt;a href="http://katas.softwarecraftsmanship.org/" target="_blank"&gt;cropping up&lt;/a&gt; &lt;a href="http://osherove.com/tdd-kata-1/" target="_blank"&gt;on the internets&lt;/a&gt;, this is my first attempt at a Keyboard Kata.&amp;#160; &lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;em&gt;The concept of the “kata” is taken from martial arts, where a series of movements are combined into a pattern.&amp;#160; Patterns are ancient, handed down from master to student over generations, and are a big part of martial art exams.&amp;#160; They often represent a visualization of defending yourself from multiple attackers, with a focus on technique, form, and strength.&amp;#160; The point is that you repeat them over and over until you master them and they become instinctive muscle memory.&amp;#160; Having done many years of Tae Kwon Do, many years ago, I still remember most of my patterns to this date.&amp;#160; Repetition is a powerful thing.&lt;/em&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;A note about my Visual Studio environment:&amp;#160; I’m using the default Visual Studio C# keyboard scheme in Visual Studio 2008.&amp;#160; I’ve unpinned all of my commonly docked windows so that they auto-hide when not in use.&amp;#160; Unpinning your tool windows not only gives you more screen real estate, but it encourages you to use keyboard sequences to open them.&lt;/p&gt;  &lt;h3&gt;Today’s Shortcut Lesson&lt;/h3&gt;  &lt;p&gt;In order to help your retention for each lesson, I’m going to &lt;em&gt;limit what you need to remember to seven simple shortcuts&lt;/em&gt;.&amp;#160; Read through the shortcuts, try out the kata, and include it in your daily routine -- memorize them and let them become muscle memory.&amp;#160; I hope to post a bunch of Katas over the next few weeks.&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;em&gt;Tip: You’ll get even better retention if you say the shortcuts out loud as you do them.&amp;#160; You’ll feel (and sound) like a complete dork, but it works.&lt;/em&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;h4&gt;Tool Windows (CTRL+W, …)&lt;/h4&gt;  &lt;p&gt;Visual Studio’s keyboard scheme does have some reason behind its madness, where related functionality are grouped with similar shortcuts.&amp;#160; The majority of the toolbar windows are grouped under CTRL+W.&amp;#160; If it helps, think &lt;em&gt;CTRL+W&lt;/em&gt;INDOW.&lt;/p&gt;  &lt;p&gt;Here are a few of the shortcuts for Tool Windows:&lt;/p&gt;  &lt;table border="1" cellspacing="0" cellpadding="2" width="400"&gt;&lt;tbody&gt;     &lt;tr&gt;       &lt;td valign="top"&gt;CTRL+W, S&lt;/td&gt;        &lt;td valign="top"&gt;Solution Explorer&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top"&gt;CTRL+W, P&lt;/td&gt;        &lt;td valign="top"&gt;Properties&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top"&gt;CTRL+W, O&lt;/td&gt;        &lt;td valign="top"&gt;Output Window&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top"&gt;CTRL+W, E&lt;/td&gt;        &lt;td valign="top"&gt;Errors&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top"&gt;CTRL+W, C&lt;/td&gt;        &lt;td valign="top"&gt;Class View&lt;/td&gt;     &lt;/tr&gt;   &lt;/tbody&gt;&lt;/table&gt;  &lt;h3&gt;&amp;#160;&lt;/h3&gt;  &lt;p&gt;Note that the ESC key will put focus in the currently opened document and auto-hide the current tool window.&lt;/p&gt;  &lt;h4&gt;Build Shortcuts&lt;/h4&gt;  &lt;table border="1" cellspacing="0" cellpadding="2" width="400"&gt;&lt;tbody&gt;     &lt;tr&gt;       &lt;td valign="top"&gt;F6          &lt;br /&gt;-or -           &lt;br /&gt;CTRL+SHIFT+B&lt;/td&gt;        &lt;td valign="top"&gt;Build Solution&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top"&gt;SHIFT+F6&lt;/td&gt;        &lt;td valign="top"&gt;Build Project&lt;/td&gt;     &lt;/tr&gt;   &lt;/tbody&gt;&lt;/table&gt;  &lt;h3&gt;&amp;#160;&lt;/h3&gt;  &lt;h3&gt;Opening a Solution Kata&lt;/h3&gt;  &lt;p&gt;So here is the kata.&amp;#160; Try this pattern every morning after you open a solution file.&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;Open the Solution Explorer. &lt;/li&gt;    &lt;li&gt;Navigate to a file &lt;/li&gt;    &lt;li&gt;View it’s properties &lt;/li&gt;    &lt;li&gt;Build the current Project &lt;/li&gt;    &lt;li&gt;Build the Solution &lt;/li&gt;    &lt;li&gt;Review the Output &lt;/li&gt;    &lt;li&gt;Check for build Errors &lt;/li&gt; &lt;/ol&gt;  &lt;p&gt;Extra credit:&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;Open a file by navigating to it in the solution explorer &lt;/li&gt;    &lt;li&gt;Open a file to a specific method in the Class View &lt;/li&gt;    &lt;li&gt;View properties of a currently opened file. &lt;/li&gt; &lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8942599-8722177094551207846?l=www.bryancook.net' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/dgsjB5DZtXkKHbszRp6hnbFWh5k/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/dgsjB5DZtXkKHbszRp6hnbFWh5k/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/dgsjB5DZtXkKHbszRp6hnbFWh5k/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/dgsjB5DZtXkKHbszRp6hnbFWh5k/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=jGm1bANfAb8:rgBcW99H67Q:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=jGm1bANfAb8:rgBcW99H67Q:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=jGm1bANfAb8:rgBcW99H67Q:G79ilh31hkQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=G79ilh31hkQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=jGm1bANfAb8:rgBcW99H67Q:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?i=jGm1bANfAb8:rgBcW99H67Q:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bryanbcook/~4/jGm1bANfAb8" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.bryancook.net/feeds/8722177094551207846/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=8942599&amp;postID=8722177094551207846" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8942599/posts/default/8722177094551207846?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8942599/posts/default/8722177094551207846?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/bryanbcook/~3/jGm1bANfAb8/visual-studio-keyboard-katas.html" title="Visual Studio Keyboard Katas" /><author><name>bryan</name><uri>http://www.blogger.com/profile/01332614158223702009</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="09074999001888279291" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.bryancook.net/2009/12/visual-studio-keyboard-katas.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0AAQX84eip7ImA9WxNaF0s.&quot;"><id>tag:blogger.com,1999:blog-8942599.post-4235207312533745846</id><published>2009-12-02T08:49:00.000-05:00</published><updated>2009-12-02T08:49:00.132-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-12-02T08:49:00.132-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term=".net" /><category scheme="http://www.blogger.com/atom/ns#" term="Visual Studio" /><category scheme="http://www.blogger.com/atom/ns#" term="TDD" /><title>NUnit for Visual Studio Addin</title><content type="html">&lt;p&gt;I recently stumbled upon this &lt;a href="http://nunitforvs.codeplex.com/" target="_blank"&gt;great addin for Visual Studio&lt;/a&gt; that uses the Visual Studio Test Adapter pattern to include NUnit tests within Visual Studio &lt;em&gt;as&lt;/em&gt; MS Tests.&amp;#160; They appear in the Test List Editor and execute equivalent to MS Test, including those handy &lt;em&gt;Run&lt;/em&gt; and &lt;em&gt;Debug&lt;/em&gt; &lt;a href="http://www.bryancook.net/2009/12/manually-creating-ms-test-project.html" target="_blank"&gt;keyboard shortcuts I described in my last post&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;Since they operate as MS Tests, the project requires some additional meta-data in the csproj file in order to have Visual Studio recognize this project as a Test library.&amp;#160; My &lt;a href="http://www.bryancook.net/2009/12/manually-creating-ms-test-project.html" target="_blank"&gt;last post has the details&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;Curious to see how far the addin could act as a stand-in for NUnit, I fired up Visual Studio, a few beers, and the NUnit attribute documentation to put it through the works.&amp;#160; I’ve compiled my findings here in the table below.&lt;/p&gt;  &lt;p&gt;In all fairness, there are a lot of attributes in NUnit, some of these you probably didn’t know existed.&lt;/p&gt;  &lt;table border="1" cellspacing="0" cellpadding="2"&gt;&lt;tbody&gt;     &lt;tr&gt;       &lt;td class="header" valign="top"&gt;NUnit Attribute&lt;/td&gt;        &lt;td class="header" valign="top"&gt;Supported&lt;/td&gt;        &lt;td class="header" valign="top"&gt;Comments&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top"&gt;&lt;a href="http://nunit.org/index.php?p=category&amp;amp;r=2.5.2" target="_blank"&gt;Category&lt;/a&gt;&lt;/td&gt;        &lt;td valign="top"&gt;No&lt;/td&gt;        &lt;td valign="top"&gt;Sadly, the addin does not register a new column definition for &lt;em&gt;Category&lt;/em&gt;.&amp;#160; Though this feature is not tied to any functional behavior, it would be greatly welcomed to improve upon Visual Studio’s Test Lists.&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top"&gt;&lt;a href="http://nunit.org/index.php?p=combinatorial&amp;amp;r=2.5.2" target="_blank"&gt;Combinatorial&lt;/a&gt;&lt;/td&gt;        &lt;td valign="top"&gt;Yes&lt;/td&gt;        &lt;td valign="top"&gt;&amp;#160;&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top"&gt;&lt;a href="http://nunit.org/index.php?p=culture&amp;amp;r=2.5.2" target="_blank"&gt;Culture / SetCulture&lt;/a&gt;&lt;/td&gt;        &lt;td valign="top"&gt;No&lt;/td&gt;        &lt;td valign="top"&gt;Tests that would normally be excluded by NUnit fail.&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top"&gt;&lt;a href="http://nunit.org/index.php?p=theory&amp;amp;r=2.5.2" target="_blank"&gt;Datapoint / Theory&lt;/a&gt;&lt;/td&gt;        &lt;td valign="top"&gt;No&lt;/td&gt;        &lt;td valign="top"&gt;Test names do not match NUnit runtime.&amp;#160; All Datapoints produce result &lt;em&gt;Not Runnable&lt;/em&gt; in the Test Results&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top"&gt;&lt;a href="http://nunit.org/index.php?p=description&amp;amp;r=2.5.2" target="_blank"&gt;Description&lt;/a&gt;&lt;/td&gt;        &lt;td valign="top"&gt;No&lt;/td&gt;        &lt;td valign="top"&gt;Value does not appear in the Test List Editor&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top"&gt;&lt;a href="http://nunit.org/index.php?p=explicit&amp;amp;r=2.5.2" target="_blank"&gt;Explicit&lt;/a&gt;&lt;/td&gt;        &lt;td valign="top"&gt;No&lt;/td&gt;        &lt;td valign="top"&gt;Explicit Tests are executed and appear as &lt;em&gt;Enabled&lt;/em&gt; in the Test List Editor&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top"&gt;&lt;a href="http://nunit.org/index.php?p=exception&amp;amp;r=2.5.2" target="_blank"&gt;ExpectedException&lt;/a&gt;&lt;/td&gt;        &lt;td valign="top"&gt;Yes&lt;/td&gt;        &lt;td valign="top"&gt;&amp;#160;&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top"&gt;&lt;a href="http://nunit.org/index.php?p=ignore&amp;amp;r=2.5.2" target="_blank"&gt;Ignore&lt;/a&gt;&lt;/td&gt;        &lt;td valign="top"&gt;Partial&lt;/td&gt;        &lt;td valign="top"&gt;Ignored tests are excluded from the Test List Editor, so they are ignored, but they do not appear as &lt;em&gt;Enabled = False&lt;/em&gt;.&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top"&gt;&lt;a href="http://nunit.org/index.php?p=maxtime&amp;amp;r=2.5.2" target="_blank"&gt;MaxTime&lt;/a&gt; / Timeout&lt;/td&gt;        &lt;td valign="top"&gt;Partial&lt;/td&gt;        &lt;td valign="top"&gt;Functions properly though supplied setting does not appear in the &lt;em&gt;Timeout&lt;/em&gt; column in the Test List Editor&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top"&gt;&lt;a href="http://nunit.org/index.php?p=platform&amp;amp;r=2.5.2" target="_blank"&gt;Platform&lt;/a&gt;&lt;/td&gt;        &lt;td valign="top"&gt;No&lt;/td&gt;        &lt;td valign="top"&gt;Tests are executed regardless of the specified platform.&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top"&gt;&lt;a href="http://nunit.org/index.php?p=property&amp;amp;r=2.5.2" target="_blank"&gt;Property&lt;/a&gt;&lt;/td&gt;        &lt;td valign="top"&gt;-&lt;/td&gt;        &lt;td valign="top"&gt;Custom properties do not appear in the output of the TRX file, which is where I’m assuming they would appear.&amp;#160; Not entirely sure if the schema would support custom properties however.&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top"&gt;&lt;a href="http://nunit.org/index.php?p=random&amp;amp;r=2.5.2" target="_blank"&gt;Random&lt;/a&gt;&lt;/td&gt;        &lt;td valign="top"&gt;No&lt;/td&gt;        &lt;td valign="top"&gt;Tests are generated, though the names contain only numbers.&amp;#160; Executing these tests produce the result &lt;em&gt;Not Runnable&lt;/em&gt; in the Test Results.&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top"&gt;&lt;a href="http://nunit.org/index.php?p=range&amp;amp;r=2.5.2" target="_blank"&gt;Range&lt;/a&gt;&lt;/td&gt;        &lt;td valign="top"&gt;No&lt;/td&gt;        &lt;td valign="top"&gt;Tests are generated, though the names contain only numbers.&amp;#160; Executing these tests produce the result &lt;em&gt;Not Runnable&lt;/em&gt; in the Test Results.&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top"&gt;&lt;a href="http://nunit.org/index.php?p=repeat&amp;amp;r=2.5.2" target="_blank"&gt;Repeat&lt;/a&gt;&lt;/td&gt;        &lt;td valign="top"&gt;Yes&lt;/td&gt;        &lt;td valign="top"&gt;&amp;#160;&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top"&gt;RequiredAddin&lt;/td&gt;        &lt;td valign="top"&gt;-&lt;/td&gt;        &lt;td valign="top"&gt;Not tested.&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top"&gt;&lt;a href="http://nunit.org/index.php?p=requiresThread&amp;amp;r=2.5.2" target="_blank"&gt;RequiresMTA / RequiresSTA / RequiresThread&lt;/a&gt;&lt;/td&gt;        &lt;td valign="top"&gt;Yes&lt;/td&gt;        &lt;td valign="top"&gt;&amp;#160;&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top"&gt;&lt;a href="http://nunit.org/index.php?p=sequential&amp;amp;r=2.5.2" target="_blank"&gt;Sequential&lt;/a&gt;&lt;/td&gt;        &lt;td valign="top"&gt;Yes&lt;/td&gt;        &lt;td valign="top"&gt;&amp;#160;&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top"&gt;&lt;a href="http://nunit.org/index.php?p=setup&amp;amp;r=2.5.2" target="_blank"&gt;Setup / Teardown&lt;/a&gt;&lt;/td&gt;        &lt;td valign="top"&gt;Yes&lt;/td&gt;        &lt;td valign="top"&gt;&amp;#160;&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top"&gt;&lt;a href="http://nunit.org/index.php?p=setupFixture&amp;amp;r=2.5.2" target="_blank"&gt;SetupFixture&lt;/a&gt;&lt;/td&gt;        &lt;td valign="top"&gt;No&lt;/td&gt;        &lt;td valign="top"&gt;Setup methods for a given namespace do not execute.&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top"&gt;Suite&lt;/td&gt;        &lt;td valign="top"&gt;-&lt;/td&gt;        &lt;td valign="top"&gt;Not tested (requires command-line switch)&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top"&gt;&lt;a href="http://nunit.org/index.php?p=fixtureSetup&amp;amp;r=2.5.2" target="_blank"&gt;TestFixtureSetup / TestFixtureTeardown&lt;/a&gt;&lt;/td&gt;        &lt;td valign="top"&gt;Yes&lt;/td&gt;        &lt;td valign="top"&gt;&amp;#160;&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top"&gt;&lt;a href="http://nunit.org/index.php?p=test&amp;amp;r=2.5.2" target="_blank"&gt;Test&lt;/a&gt;&lt;/td&gt;        &lt;td valign="top"&gt;Yes&lt;/td&gt;        &lt;td valign="top"&gt;Of course!&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top"&gt;&lt;a href="http://nunit.org/index.php?p=testCase&amp;amp;r=2.5.2" target="_blank"&gt;TestCase&lt;/a&gt;&lt;/td&gt;        &lt;td valign="top"&gt;Yes&lt;/td&gt;        &lt;td valign="top"&gt;Tested different argument types (int, string), TestName, ExpectedException.&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top"&gt;&lt;a href="http://nunit.org/index.php?p=testCaseSource&amp;amp;r=2.5.2" target="_blank"&gt;TestCaseSource&lt;/a&gt;&lt;/td&gt;        &lt;td valign="top"&gt;Yes&lt;/td&gt;        &lt;td valign="top"&gt;&amp;#160;&lt;/td&gt;     &lt;/tr&gt;   &lt;/tbody&gt;&lt;/table&gt;  &lt;p&gt;There’s quite a few &lt;em&gt;No’&lt;/em&gt;s in this list, but the major players (&lt;em&gt;Test, Setup/Teardown, TestFixtureSetup/Teardown&lt;/em&gt;) are functional.&amp;#160; I’m actually pleased that NUnit 2.5.2 features such as parameterized tests (&lt;em&gt;TestCase, TestCaseSource&lt;/em&gt;) and &lt;em&gt;Combinatorial / Sequential / Values&lt;/em&gt; are in place, as well as former addin features that were bundled into the framework (MaxTime / Repeat).&lt;/p&gt;  &lt;p&gt;In respect to the malformed test names and non-runnable tests for the &lt;em&gt;Theory / Range / Random&lt;/em&gt; attributes, hopefully this is a small issue that can be resolved.&amp;#160; The cosmetic issues with &lt;em&gt;Ignore / Description / Category&lt;/em&gt; don’t pose any major concerns though they would be large wins in terms of full compatibility with MS Test user interface and features.&lt;/p&gt;  &lt;p&gt;I’ve never used the SetupFixture nor the culture attributes, so I’m not losing much sleep over these.&lt;/p&gt;  &lt;p&gt;However, for me, the main issue for me is that &lt;em&gt;Explicit&lt;/em&gt; tests are always executed.&amp;#160; I’ve worked on many projects where a handful of tests either brought down the build server or couldn’t be run with other tests.&amp;#160; Rather than solve the problem, developers tagged the tests as Explicit – they work, but you better have a good reason to be running them.&lt;/p&gt;  &lt;p&gt;Hats off to the NUnitForVS team.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8942599-4235207312533745846?l=www.bryancook.net' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/Co3KFsBTXwwA1NTZYO3i-f_rgRk/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/Co3KFsBTXwwA1NTZYO3i-f_rgRk/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/Co3KFsBTXwwA1NTZYO3i-f_rgRk/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/Co3KFsBTXwwA1NTZYO3i-f_rgRk/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=WQ6bwva6k-w:BuVyg1-grRg:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=WQ6bwva6k-w:BuVyg1-grRg:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=WQ6bwva6k-w:BuVyg1-grRg:G79ilh31hkQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=G79ilh31hkQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=WQ6bwva6k-w:BuVyg1-grRg:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?i=WQ6bwva6k-w:BuVyg1-grRg:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bryanbcook/~4/WQ6bwva6k-w" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.bryancook.net/feeds/4235207312533745846/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=8942599&amp;postID=4235207312533745846" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8942599/posts/default/4235207312533745846?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8942599/posts/default/4235207312533745846?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/bryanbcook/~3/WQ6bwva6k-w/nunit-for-visual-studio-addin.html" title="NUnit for Visual Studio Addin" /><author><name>bryan</name><uri>http://www.blogger.com/profile/01332614158223702009</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="09074999001888279291" /></author><thr:total>1</thr:total><feedburner:origLink>http://www.bryancook.net/2009/12/nunit-for-visual-studio-addin.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUMMQnY7cSp7ImA9WxBSEUk.&quot;"><id>tag:blogger.com,1999:blog-8942599.post-5405631157959187526</id><published>2009-12-01T08:34:00.001-05:00</published><updated>2009-12-18T09:44:43.809-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-12-18T09:44:43.809-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Tips" /><category scheme="http://www.blogger.com/atom/ns#" term=".net" /><category scheme="http://www.blogger.com/atom/ns#" term="TDD" /><title>Manually creating a MS Test Project</title><content type="html">&lt;p&gt;Although I’ve always been a huge proponent of NUnit, I’m finding I’m using MS Test more frequently for the following reasons:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;My organization is a large Microsoft partner, so there’s often some preference for Microsoft tools in our projects. &lt;/li&gt;    &lt;li&gt;Support for open source tools is a concern for some organizations I work with.&amp;#160; Although Tests are not part of the production deliverables, some organizations are very risk adverse and reasonably do not want to tie themselves to products without support or guarantee of backward compatibility. &lt;/li&gt;    &lt;li&gt;Severe &lt;a href="http://www.jetbrains.com/resharper/index.html" target="_blank"&gt;Resharper&lt;/a&gt; withdrawal.&amp;#160; After spending several years with Resharper tools, I’ve spent the last year with a barebones Visual Studio 2008 installation.&amp;#160; Without the tight integration between Visual Studio and NUnit, attaching and debugging a process isn’t involuntary. If the &lt;a href="http://www.jetbrains.com/" target="_blank"&gt;JetBrains&lt;/a&gt; guys are listening, hook me up. &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Reluctantly, I’ve started to use Visual Studio Test warts-n-all.&amp;#160; Out of the box, MS Test has two very handy keyboard shortcuts where you can either &lt;em&gt;Run (CTRL+R, T)&lt;/em&gt; or &lt;em&gt;Debug (CTRL+R, CTRL+T)&lt;/em&gt; the current test, fixture or solution, depending on where your mouse is currently focused.&amp;#160; &lt;/p&gt;  &lt;p&gt;Oddly enough, I’ve found myself in a position where I’ve manually created a Test project by adding the appropriate references, but none of the Visual Studio Test features work, including these handy short cuts.&amp;#160; Any attempt to run these tests using these shortcut produces an error:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;em&gt;No tests were run because no tests were loaded or the selected tests are disabled.&lt;/em&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;This error is produced because the Test Adapter is looking for a few meta attributes in the project that are added when you using the &lt;em&gt;New Test Project&lt;/em&gt; template.&lt;/p&gt;  &lt;p&gt;To manually create a MS Test project, in Visual Studio:&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;Create a new Class Library project &lt;/li&gt;    &lt;li&gt;Add a reference to: &lt;em&gt;Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll&lt;/em&gt; &lt;/li&gt;    &lt;li&gt;Right-click your project and choose “Unload Project”.      &lt;br /&gt;      &lt;br /&gt;&lt;a href="http://lh4.ggpht.com/_XB6P2gWz--8/SxSq2vypRdI/AAAAAAAAAb8/eYNDuMAGsBs/s1600-h/unload-project%5B3%5D.png"&gt;&lt;img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="unload-project" border="0" alt="unload-project" src="http://lh5.ggpht.com/_XB6P2gWz--8/SxSq3j8elyI/AAAAAAAAAcA/bWA9OTJMXVM/unload-project_thumb%5B1%5D.png?imgmax=800" width="342" height="484" /&gt;&lt;/a&gt;       &lt;br /&gt;&lt;/li&gt;    &lt;li&gt;Right-click on your project and choose “&lt;em&gt;Edit &amp;lt;ProjectName&amp;gt;”        &lt;br /&gt;        &lt;br /&gt;&lt;a href="http://lh6.ggpht.com/_XB6P2gWz--8/SxSq3zmG6bI/AAAAAAAAAcE/SyJnVUG0lqA/s1600-h/edit-project%5B12%5D.png"&gt;&lt;img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="edit-project" border="0" alt="edit-project" src="http://lh4.ggpht.com/_XB6P2gWz--8/SxSq4bfqjcI/AAAAAAAAAcI/mVNndZmuh1Y/edit-project_thumb%5B8%5D.png?imgmax=800" width="342" height="160" /&gt;&lt;/a&gt;         &lt;br /&gt;&lt;/em&gt;&lt;/li&gt;    &lt;li&gt;Add the following &lt;em&gt;ProjectTypeGuids&lt;/em&gt; element to the first &lt;em&gt;ProjectGroup&lt;/em&gt; element:       &lt;br /&gt;      &lt;br /&gt;      &lt;pre class="brush: xml; gutter: false; highlight: [13];"&gt;  &amp;lt;PropertyGroup&amp;gt;
    &amp;lt;Configuration Condition=&amp;quot; '$(Configuration)' == '' &amp;quot;&amp;gt;Debug&amp;lt;/Configuration&amp;gt;
    &amp;lt;Platform Condition=&amp;quot; '$(Platform)' == '' &amp;quot;&amp;gt;AnyCPU&amp;lt;/Platform&amp;gt;
    &amp;lt;ProductVersion&amp;gt;9.0.30729&amp;lt;/ProductVersion&amp;gt;
    &amp;lt;SchemaVersion&amp;gt;2.0&amp;lt;/SchemaVersion&amp;gt;
    &amp;lt;ProjectGuid&amp;gt;{4D38A077-23EE-4E9F-876A-43C33433FFEB}&amp;lt;/ProjectGuid&amp;gt;
    &amp;lt;OutputType&amp;gt;Library&amp;lt;/OutputType&amp;gt;
    &amp;lt;AppDesignerFolder&amp;gt;Properties&amp;lt;/AppDesignerFolder&amp;gt;
    &amp;lt;RootNamespace&amp;gt;Example.ManualTestProject&amp;lt;/RootNamespace&amp;gt;
    &amp;lt;AssemblyName&amp;gt;Example.ManualTestProject&amp;lt;/AssemblyName&amp;gt;
    &amp;lt;TargetFrameworkVersion&amp;gt;v3.5&amp;lt;/TargetFrameworkVersion&amp;gt;
    &amp;lt;FileAlignment&amp;gt;512&amp;lt;/FileAlignment&amp;gt;
    &amp;lt;ProjectTypeGuids&amp;gt;{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}&amp;lt;/ProjectTypeGuids&amp;gt;
  &amp;lt;/PropertyGroup&amp;gt;&lt;/pre&gt;
  &lt;/li&gt;

  &lt;li&gt;Right-click on the Project and choose &amp;quot;&lt;i&gt;Reload &amp;lt;Project Name&amp;gt;&lt;/i&gt;&amp;quot; &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once reloaded, the handy shortcuts work as expected.&lt;/p&gt;

&lt;p&gt;Note for the curious:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Guid {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}: refers to C# project &lt;/li&gt;

  &lt;li&gt;Guid {3AC096D0-A1C2-E12C-1390-A8335801FDAB}: refers to the “Test Project Flavor” &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;From my limited understanding of how Visual Studio Test package works, it scans the solution looking for projects that can contain tests.&amp;#160; Without the magic ProjectTypeGuid, the class library is excluded from this process.&lt;/p&gt;

&lt;p&gt;Happy coding.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8942599-5405631157959187526?l=www.bryancook.net' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/3h_9plWaYbjcCEIRfRao3G-oSr0/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/3h_9plWaYbjcCEIRfRao3G-oSr0/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/3h_9plWaYbjcCEIRfRao3G-oSr0/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/3h_9plWaYbjcCEIRfRao3G-oSr0/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=NXy_RgXDU1g:LzgPfbtKPww:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=NXy_RgXDU1g:LzgPfbtKPww:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=NXy_RgXDU1g:LzgPfbtKPww:G79ilh31hkQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=G79ilh31hkQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=NXy_RgXDU1g:LzgPfbtKPww:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?i=NXy_RgXDU1g:LzgPfbtKPww:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bryanbcook/~4/NXy_RgXDU1g" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.bryancook.net/feeds/5405631157959187526/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=8942599&amp;postID=5405631157959187526" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8942599/posts/default/5405631157959187526?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8942599/posts/default/5405631157959187526?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/bryanbcook/~3/NXy_RgXDU1g/manually-creating-ms-test-project.html" title="Manually creating a MS Test Project" /><author><name>bryan</name><uri>http://www.blogger.com/profile/01332614158223702009</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="09074999001888279291" /></author><thr:total>2</thr:total><feedburner:origLink>http://www.bryancook.net/2009/12/manually-creating-ms-test-project.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUAGRX0zcSp7ImA9WxNbGUQ.&quot;"><id>tag:blogger.com,1999:blog-8942599.post-6876431149499884981</id><published>2009-11-23T08:43:00.000-05:00</published><updated>2009-11-23T12:35:24.389-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-11-23T12:35:24.389-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="selenium toolkit" /><category scheme="http://www.blogger.com/atom/ns#" term=".net" /><title>User Extensions with the Selenium Toolkit for .NET</title><content type="html">&lt;p&gt;As I mentioned &lt;a href="http://www.bryancook.net/2009/11/selenium-toolkit-for-net-081-now.html" target="_blank"&gt;in my last post&lt;/a&gt;, new to version 0.81 the &lt;a href="http://seleniumtoolkit.codeplex.com" target="_blank"&gt;Selenium Toolkit for .NET&lt;/a&gt; now provides a simple mechanism to add user-defined extensions to Selenium.&lt;/p&gt;  &lt;h3&gt;Some background&lt;/h3&gt;  &lt;p&gt;A user-extension for Selenium is a JavaScript file that becomes embedded into Selenium’s Test Runner.&amp;#160; Typically, the script extends Selenium’s core JavaScript to add new commands, but it also can be used to extend existing functionality, or add custom locator strategies.&amp;#160; Both &lt;a href="http://seleniumhq.org/docs/03_selenium_ide.html#user-extensions" target="_blank"&gt;Selenium IDE&lt;/a&gt; and &lt;a href="http://seleniumhq.org/docs/08_user_extensions.html#using-user-extensions-with-selenium-rc" target="_blank"&gt;RC allow extensions&lt;/a&gt; to be added.&lt;/p&gt; For the purposes of this discussion, here’s a crude example of a selenium extension:&amp;#160; &lt;pre class="brush: js; gutter: false;"&gt;Selenium.prototype.getMyValue = function(locator, text) {
    return locator;
};&lt;/pre&gt;

&lt;p&gt;The toolkit takes a straight forward approach to including extensions: it combines the files listed in the configuration file (in order of appearance) into a single file and then configures the Selenium RC to use it.&amp;#160; &lt;/p&gt;

&lt;blockquote&gt;&lt;em&gt;Incidentally, if you ever need to verify that your extensions were loaded, you can simply view the JavaScript file at this url: &lt;/em&gt;&lt;a href="http://localhost:4444/selenium-server/core/scripts/user-extensions.js"&gt;http://localhost:4444/selenium-server/core/scripts/user-extensions.js&lt;/a&gt; &lt;/blockquote&gt;

&lt;p&gt;The &lt;em&gt;ISelenium&lt;/em&gt; interface of the Selenium API does not directly expose a mechanism to execute custom commands.&amp;#160; Instead, we need to send our custom command using a command processor (&lt;em&gt;ICommandProcessor&lt;/em&gt;), which isn’t exposed by default so a custom implementation of the ISelenium interface is required.&amp;#160; The Selenium documentation provides a &lt;a href="http://seleniumhq.org/docs/08_user_extensions.html#using-user-extensions-with-selenium-rc" target="_blank"&gt;pretty good overview&lt;/a&gt; of how to do this, and the remainder of this post demonstrates how to integrate this approach with the toolkit.&lt;/p&gt;

&lt;h3&gt;Create a customized ISelenium&lt;/h3&gt;

&lt;p&gt;One of the new features added to the toolkit in this release is the ability to supply your own mechanism for instantiating the &lt;em&gt;ISelenium&lt;/em&gt; instance. The factory is a basic class with a &lt;em&gt;Create&lt;/em&gt; method.&amp;#160; Simply implement the &lt;em&gt;ISeleniumFactoryProvider&lt;/em&gt; interface and then wire it up into the configuration settings &lt;em&gt;(see below).&lt;/em&gt;&amp;#160; If no setting is provided in the config, a default factory is used.&lt;/p&gt;

&lt;p&gt;This example below shows a custom &lt;em&gt;ISeleniumFactoryProvider&lt;/em&gt; that creates a customized ISelenium instance that exposes the &lt;em&gt;ICommandProcessor:&lt;/em&gt;&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;namespace Custom
{
    using Selenium;
    using SeleniumToolkit.Core;

    public class SeleniumFactory : ISeleniumFactoryProvider
    {
        public ISelenium Create(string host,
                                int port,
                                string browserProfile,
                                string baseUrl)
         {
             ICommandProcessor processor = new HttpCommandProcessor(host, port, browserProfile, baseUrl);
             return new CustomSelenium(processor);       
         }
    }

    public class CustomSelenium : DefaultSelenium
    {
        public CustomSelenium(ICommandProcessor processor) 
            : base(processor)
        {
            CommandProcessor = processor
        }

        public ICommandProcessor CommmandProcessor
        {
            get;
            protected set;
        }
    }
}&lt;/pre&gt;

&lt;h3&gt;Tweak your Settings&lt;/h3&gt;

&lt;p&gt;After you’ve compiled your custom factory, you’ll need to reference your JavaScript file in the &lt;em&gt;userExtensions&lt;/em&gt; element and specify your custom factory using the &lt;strong&gt;&lt;em&gt;factoryType&lt;/em&gt;&lt;/strong&gt; attribute of the Selenium Node.&lt;/p&gt;

&lt;pre class="brush: xml;"&gt;&amp;lt;Selenium
    factoryType=&amp;quot;Custom.SeleniumFactory, CustomProject&amp;quot;
    &amp;gt;
   &amp;lt;userExtensions&amp;gt;
       &amp;lt;add name=&amp;quot;example&amp;quot;
            path=&amp;quot;myextension.js&amp;quot; /&amp;gt;
   &amp;lt;/userExtensions&amp;gt;
&amp;lt;/Selenium&amp;gt;&lt;/pre&gt;

&lt;h3&gt;Putting it all together&lt;/h3&gt;

&lt;p&gt;With the configuration settings in place, now all we need to do is cast the &lt;em&gt;Browser.Current&lt;/em&gt; instance to our custom type.&lt;/p&gt;

&lt;pre class="brush: csharp;"&gt;namespace Custom.Test
{
    using NUnit.Framework;
    using Selenium;
    using SeleniumToolkit;

    [WebFixture]
    public class Example
    {
        [WebTest]
        public void ShowUsage()
        {
            CustomSelenium browser = Browser.Current as CustomSelenium;

            string[] inputArgs = { &amp;quot;Hello World&amp;quot; };
            string result = browser.CommandProcessor.DoCommand(&amp;quot;getMyValue&amp;quot;, inputArgs);
            Assert.AreEqual(&amp;quot;Hello World&amp;quot;, result);
        }
    }
}&lt;/pre&gt;

&lt;p&gt;Happy coding.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8942599-6876431149499884981?l=www.bryancook.net' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/ZibPHlVw7G7xTVL3ELoU9151rvE/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/ZibPHlVw7G7xTVL3ELoU9151rvE/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/ZibPHlVw7G7xTVL3ELoU9151rvE/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/ZibPHlVw7G7xTVL3ELoU9151rvE/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=FhbGT3_CCRM:GJyGD5KfJMc:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=FhbGT3_CCRM:GJyGD5KfJMc:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=FhbGT3_CCRM:GJyGD5KfJMc:G79ilh31hkQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=G79ilh31hkQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=FhbGT3_CCRM:GJyGD5KfJMc:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?i=FhbGT3_CCRM:GJyGD5KfJMc:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bryanbcook/~4/FhbGT3_CCRM" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.bryancook.net/feeds/6876431149499884981/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=8942599&amp;postID=6876431149499884981" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8942599/posts/default/6876431149499884981?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8942599/posts/default/6876431149499884981?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/bryanbcook/~3/FhbGT3_CCRM/user-extensions-with-selenium-toolkit.html" title="User Extensions with the Selenium Toolkit for .NET" /><author><name>bryan</name><uri>http://www.blogger.com/profile/01332614158223702009</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="09074999001888279291" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.bryancook.net/2009/11/user-extensions-with-selenium-toolkit.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEAGQ3w4fyp7ImA9WxNbFks.&quot;"><id>tag:blogger.com,1999:blog-8942599.post-4887391262602192715</id><published>2009-11-19T16:36:00.001-05:00</published><updated>2009-11-19T16:38:42.237-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-11-19T16:38:42.237-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="selenium toolkit" /><category scheme="http://www.blogger.com/atom/ns#" term=".net" /><title>Selenium Toolkit for .NET 0.81 Now Available</title><content type="html">&lt;p&gt;I’m happy to announce the next release for the &lt;a href="http://seleniumtoolkit.codeplex.com" target="_blank"&gt;Selenium Toolkit for .NET&lt;/a&gt; is &lt;a href="http://seleniumtoolkit.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=33997" target="_blank"&gt;now available for download&lt;/a&gt;.&amp;#160; This release adds minor fixes to the runtime and enhanced configuration support.&lt;/p&gt;  &lt;h3&gt;New for this release&lt;/h3&gt;  &lt;p&gt;This release includes several new enhancements:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;NUnit 2.5.2 Support &lt;/li&gt;    &lt;li&gt;Custom Browser Profiles and Aliases &lt;/li&gt;    &lt;li&gt;User Extensions support &lt;/li&gt;    &lt;li&gt;Proxy Server support &lt;/li&gt; &lt;/ul&gt;  &lt;h4&gt;NUnit 2.5.2 Support&lt;/h4&gt;  &lt;p&gt;This release represents an upgrade for the NUnit addin to latest version NUnit (2.5.2).&amp;#160; NUnit 2.5.2 includes several performance and user interface enhancements which improve the development experience, including data driven tests and source-code browser for exceptions.&lt;/p&gt;  &lt;p&gt;As part of a dependency issue with the NUnit framework, addins are specific to the runtime they’re compiled against, so you must have NUnit 2.5.2 installed in order to use the addin.&amp;#160; If you have NUnit 2.4.8, you’ll have to upgrade.&amp;#160; If you haven’t been able to use the toolkit because you weren’t using NUnit 2.4.8, &lt;a href="http://seleniumtoolkit.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=33997" target="_blank"&gt;please download&lt;/a&gt; and send in some feedback.&lt;/p&gt;  &lt;h4&gt;Custom Browser Profiles and Aliases&lt;/h4&gt;  &lt;p&gt;One of the key goals of the toolkit is to minimize duplication and environment specific settings in your tests so that the impact of moving between environments is minimized.&amp;#160; The toolkit now supports the concept of &lt;em&gt;browser aliases&lt;/em&gt; where custom profiles can be defined as a lookup table in the configuration settings.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Before:&lt;/strong&gt;&lt;/p&gt;  &lt;pre class="brush: csharp; gutter: false; toolbar: false;"&gt;[WebFixture]
public class ExampleTest
{
    [WebTest(DefaultBrowser=@&amp;quot;*custom \&amp;quot;C:\PathToFireFox\FireFox.exe\&amp;quot; -no-remote -profile \&amp;quot;C:\PathToProfile\&amp;quot;&amp;quot;)]
    public void OpenCustomProfile()
    {
        // ....
    }
}&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;After:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The dependency on the physical folder path can now be externalized to the configuration file, which can be modified to suit each environment without having to alter the tests.&lt;/p&gt;

&lt;pre class="brush: csharp; gutter: false; toolbar: false;"&gt;[WebFixture]
public void ExampleWithBrowserAlias
{
    [WebTest(DefaultBrowser=&amp;quot;ff-profile-1&amp;quot;)]
    public void OpenCustomProfile()
    {
        // ....
    }
}&lt;/pre&gt;

&lt;p&gt;And the configuration settings....&lt;/p&gt;

&lt;pre class="brush: xml; gutter: false; toolbar: false;"&gt;&amp;lt;configuration&amp;gt;
    &amp;lt;!-- snip --&amp;gt;

    &amp;lt;Selenium
        BrowserUrl=&amp;quot;http://www.bryancook.net&amp;quot;
        &amp;gt;
        &amp;lt;browsers&amp;gt;
            &amp;lt;add key=&amp;quot;ff-profile-1&amp;quot;
                 value=&amp;quot;*custom &amp;amp;quot;C:\FireFoxPath&amp;amp;quot; -no-remote -profile &amp;amp;quot;PathToProfile&amp;amp;quot;&amp;quot;
                /&amp;gt;
        &amp;lt;/browsers&amp;gt;
    &amp;lt;/Selenium&amp;gt;

&amp;lt;/configuration&amp;gt;&lt;/pre&gt;

&lt;h4&gt;User Extensions support&lt;/h4&gt;

&lt;p&gt;Selenium provides an extensibility model that allows you to extend its functionality through custom JavaScript files.&amp;#160; This release provides two key enhancements to enable this functionality: &lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Configuration element that allows you to specify JavaScript files and directories to be concatenated into the &lt;em&gt;user-extensions.js&lt;/em&gt; file; and &lt;/li&gt;

  &lt;li&gt;Factory model that allows you to create your own implementations of the &lt;em&gt;ISelenium&lt;/em&gt; interface.&amp;#160; A custom implementation allows you to execute your own custom commands, and will likely play an integral role in the WebDriver backed Selenium implementation planned for Selenium 2.0.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Expect a blog post soon that demonstrates this functionality, but in the meantime you can find details on the &lt;a href="http://seleniumtoolkit.codeplex.com/wikipage?title=Selenium%20Toolkit%20Configuration%20Settings" target="_blank"&gt;configuration settings here&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;Proxy Server Support&lt;/h4&gt;

&lt;p&gt;While not as exciting or as interesting as custom user extensions, this release includes some additional configuration settings for users running Selenium-RC against a web-site that is only accessible through a proxy server.&amp;#160; See the &lt;a href="http://seleniumtoolkit.codeplex.com/wikipage?title=Selenium%20Toolkit%20Configuration%20Settings" target="_blank"&gt;configuration settings&lt;/a&gt;&amp;#160; for more details.&lt;/p&gt;

&lt;h3&gt;Download now&lt;/h3&gt;

&lt;p&gt;Enough already!&amp;#160; Go &lt;a href="http://seleniumtoolkit.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=33997" target="_blank"&gt;download the latest release&lt;/a&gt; and try it out.&amp;#160; As always, feedback is always welcome.&lt;/p&gt;

&lt;p&gt;Happy coding.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8942599-4887391262602192715?l=www.bryancook.net' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/gK5DAzFooT45esqs3w7s04Ezyds/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/gK5DAzFooT45esqs3w7s04Ezyds/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/gK5DAzFooT45esqs3w7s04Ezyds/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/gK5DAzFooT45esqs3w7s04Ezyds/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=SepEoQIDgMc:VHR9SXWypmI:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=SepEoQIDgMc:VHR9SXWypmI:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=SepEoQIDgMc:VHR9SXWypmI:G79ilh31hkQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=G79ilh31hkQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=SepEoQIDgMc:VHR9SXWypmI:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?i=SepEoQIDgMc:VHR9SXWypmI:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bryanbcook/~4/SepEoQIDgMc" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.bryancook.net/feeds/4887391262602192715/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=8942599&amp;postID=4887391262602192715" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8942599/posts/default/4887391262602192715?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8942599/posts/default/4887391262602192715?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/bryanbcook/~3/SepEoQIDgMc/selenium-toolkit-for-net-081-now.html" title="Selenium Toolkit for .NET 0.81 Now Available" /><author><name>bryan</name><uri>http://www.blogger.com/profile/01332614158223702009</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="09074999001888279291" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.bryancook.net/2009/11/selenium-toolkit-for-net-081-now.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkUEQXoyeSp7ImA9WxNUGUg.&quot;"><id>tag:blogger.com,1999:blog-8942599.post-8904216749620517289</id><published>2009-11-11T11:50:00.000-05:00</published><updated>2009-11-11T11:50:00.491-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-11-11T11:50:00.491-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Tips" /><category scheme="http://www.blogger.com/atom/ns#" term="blogging" /><title>Add Syntax Highlighter to Live Writer Templates</title><content type="html">&lt;p&gt;Been tweaking my blog template slowly over the past year, and while I’ve got plans to switch to &lt;a href="http://960.gs/" target="_blank"&gt;960.gs format&lt;/a&gt;, my most recent change has been how I highlight my code snippets: like everyone else on the planet, I’m now using &lt;a href="http://alexgorbatchev.com" target="_blank"&gt;Alex Gorbatchev’s&lt;/a&gt; &lt;a href="http://alexgorbatchev.com/wiki/SyntaxHighlighter" target="_blank"&gt;SyntaxHighlighter&lt;/a&gt; in combination with &lt;a href="http://precode.codeplex.com/" target="_blank"&gt;Anthony Bouch’s PreCode&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;Up to this point, I’ve been using &lt;a href="http://www.manoli.net/csharpformat/" target="_blank"&gt;CSharpFormat&lt;/a&gt; with &lt;a href="http://www.shahine.com/omar/" target="_blank"&gt;Omar Shahine’s&lt;/a&gt; &lt;a href="http://www.shahine.com/omar/InsertCodeForWindowsLiveWriter.aspx" target="_blank"&gt;Insert Code plugin&lt;/a&gt;.&amp;#160; This solution has worked well for me up to this point, but it’s the features of SyntaxHighlighter (line numbers, collapsible blocks, etc) and the quality of PreCode’s editing features that have made me switch.&lt;/p&gt;  &lt;p&gt;The key difference between the two solutions is that CSharpFormat adornes your code with HTML blocks that are immediately visible in Live Writer (and my RSS feed); SyntaxHighlighter highlights your code at runtime using JavaScript.&lt;/p&gt;  &lt;p&gt;While I can live without the RSS feed highlighting, I really miss seeing my code highlighted in Live Writer.&amp;#160; After updating my blogger template, I noticed that Live Writer ignores your JavaScript when it updates your editing template.&lt;/p&gt;  &lt;p&gt;Fortunately, Live Writer’s editing template is stored locally as HTML, here:&lt;/p&gt;  &lt;pre&gt;C:\Users\bcook\AppData\Roaming\Windows Live Writer\blogtemplates\&amp;lt;guid&amp;gt;\index.html&lt;/pre&gt;

&lt;p&gt;Simply open the file in your editor, and paste in your JavaScript:&lt;/p&gt;

&lt;pre class="brush: js; gutter: false;"&gt;&amp;lt;!-- syntax highlighter --&amp;gt; 
&amp;lt;script src='http://alexgorbatchev.com/pub/sh/2.0.320/scripts/shCore.js' type='text/javascript'&amp;gt;&amp;lt;/script&amp;gt; 
&amp;lt;script src='http://alexgorbatchev.com/pub/sh/2.0.320/scripts/shBrushCSharp.js' type='text/javascript'&amp;gt;&amp;lt;/script&amp;gt; 
&amp;lt;script src='http://alexgorbatchev.com/pub/sh/2.0.320/scripts/shBrushCss.js' type='text/javascript'&amp;gt;&amp;lt;/script&amp;gt; 
&amp;lt;script src='http://alexgorbatchev.com/pub/sh/2.0.320/scripts/shBrushJScript.js' type='text/javascript'&amp;gt;&amp;lt;/script&amp;gt; 
&amp;lt;script src='http://alexgorbatchev.com/pub/sh/2.0.320/scripts/shBrushXml.js' type='text/javascript'&amp;gt;&amp;lt;/script&amp;gt; 
&amp;lt;script type='text/javascript'&amp;gt; 
  SyntaxHighlighter.config.bloggerMode = true;
  SyntaxHighlighter.config.clipboardSwf = 'http://alexgorbatchev.com/pub/sh/2.0.320/scripts/clipboard.swf';
  SyntaxHighlighter.all();
&amp;lt;/script&amp;gt;&lt;/pre&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8942599-8904216749620517289?l=www.bryancook.net' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/WrrwWs-n5pvgR0aatuzGdIzQQLc/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/WrrwWs-n5pvgR0aatuzGdIzQQLc/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/WrrwWs-n5pvgR0aatuzGdIzQQLc/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/WrrwWs-n5pvgR0aatuzGdIzQQLc/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=ZqejcgGFsHA:WBEHd1kkRKs:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=ZqejcgGFsHA:WBEHd1kkRKs:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=ZqejcgGFsHA:WBEHd1kkRKs:G79ilh31hkQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=G79ilh31hkQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=ZqejcgGFsHA:WBEHd1kkRKs:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?i=ZqejcgGFsHA:WBEHd1kkRKs:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bryanbcook/~4/ZqejcgGFsHA" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.bryancook.net/feeds/8904216749620517289/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=8942599&amp;postID=8904216749620517289" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8942599/posts/default/8904216749620517289?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8942599/posts/default/8904216749620517289?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/bryanbcook/~3/ZqejcgGFsHA/add-syntax-highlighter-to-live-writer.html" title="Add Syntax Highlighter to Live Writer Templates" /><author><name>bryan</name><uri>http://www.blogger.com/profile/01332614158223702009</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="09074999001888279291" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.bryancook.net/2009/11/add-syntax-highlighter-to-live-writer.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CU4GQ3c6cCp7ImA9WxNUGEs.&quot;"><id>tag:blogger.com,1999:blog-8942599.post-6377406566472727366</id><published>2009-11-10T01:48:00.001-05:00</published><updated>2009-11-10T09:38:42.918-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-11-10T09:38:42.918-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term=".net" /><title>Implementing a Strategy Pattern using the Unity Framework</title><content type="html">&lt;p&gt;My current project is using Unity and has had some interesting challenges to work through.&amp;#160; One of the design challenges we encountered involved several components that were nearly identical in appearance except for a few minor functional details.&amp;#160; We decided to implement a strategy pattern where the variances were different strategies that were applied to common context. &lt;/p&gt;  &lt;p&gt;For the sake of argument, let’s say we’re talking about a Financial Calculator that supports different types of financial calculations (FPV, Payment, etc)&lt;/p&gt;  &lt;pre class="brush: js; gutter: false; toolbar: false;"&gt;public class Calculator : ICalculator
{
    public Calculator(IFormula formula)
    {
	_formula = formula;
    }

    public CalcResult Calculate(CalcArgs input)
    {
        return _formula.Process(input);
    }

    IFormula _formula;
}

public interface IFormula
{
    CalcResult Process(CalcArgs input);
}

public class FutureValue : IFormula
{
    public CalcResult Process(CalcArgs input)
    {
        // ...
    }
}

public class PresentValue: IFormula
{
    public CalcResult Process(CalcArgs input)
    {
        // ...
    }
}&lt;/pre&gt;

&lt;p&gt;In &lt;a href="http://www.bryancook.net/2009/11/simple-dependency-injection-using-unity.html" target="_blank"&gt;my last post, I showed how Unity needs some additional configuration&lt;/a&gt; in order to resolve ambiguous types – Unit won’t be able to resolve our &lt;em&gt;Calculator&lt;/em&gt; unless we specify which &lt;em&gt;IFormula&lt;/em&gt; we want to use.&amp;#160; This scenario is a bit different because we want to be able to use both the FutureValue and PresentValue formulas at runtime.&amp;#160; &lt;/p&gt;

&lt;p&gt;We have a couple of options to choose from, each with their own pitfalls:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Container overloading &lt;/li&gt;

  &lt;li&gt;Named Instances &lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Container Overloading&lt;/h3&gt;

&lt;p&gt;At a basic level, we’re limited to registering a type only once per container.&amp;#160; Fortunately, Unity supports the ability to spawn a child container that can used to replace registered types with alternate registrations.&amp;#160; By swapping out our types, we can create alternate runtimes that can be scoped to specific areas of our application.&lt;/p&gt;

&lt;pre class="brush: js; gutter: false; toolbar: false;"&gt;[TestMethod]
public void CanOverloadContainer()
{
    // create the container and register the
    //    PresentValue formula
    var container = new UnityContainer();
    container.RegisterType&amp;lt;IFormula, PresentValue&amp;gt;();

    // verify we can resolve PresentValue
    var formula1 = container.Resolve&amp;lt;IFormula&amp;gt;();
    Assert.IsInstanceOfType(formula1, typeof(PresentValue));

    // create a child container, and replace
    //    the formula with FutureValue formula
    var childContainer = container.CreateChildContainer();
    childContainer.RegisterType&amp;lt;IFormula, FutureValue&amp;gt;();

    // verify that we resolve the new formula
    var formula2 = childContainer.Resolve&amp;lt;IFormula&amp;gt;();
    Assert.IsInstanceOfType(formula2, typeof(FutureValue));
}&lt;/pre&gt;

&lt;p&gt;This approach provides a clean separation between the container and the various combinations of our strategy pattern.&amp;#160; If you can compose your application of controllers that can extend and manipulate child containers, this is your best bet as each container provides a small targeted service to the application as a whole.&lt;/p&gt;

&lt;p&gt;However, if the variations must live in close contact with one another and you can’t separate them into different containers, you’ll need a different strategy.&lt;/p&gt;

&lt;h3&gt;Named Instances&lt;/h3&gt;

&lt;p&gt;Unity provides the ability to register a type more than once, with a caveat that each type is given a unique name within the container.&amp;#160; This feels more like a hack than a best practice, but it provides a simple solution to registering the various combinations of &lt;em&gt;ICalculator&lt;/em&gt; types side-by-side.&amp;#160; Since our&lt;em&gt; &lt;/em&gt;variations are based on the types passed in through the constructor, we can provide Unity the construction information it needs using &lt;a href="http://msdn.microsoft.com/en-us/library/cc440940.aspx" target="_blank"&gt;InjectionConstructor and ResolvedParameter&lt;/a&gt;&lt;em&gt;&lt;/em&gt; objects.&amp;#160; To resolve our objects, we must specify the unique name when resolving.&lt;/p&gt;

&lt;pre class="brush: js; gutter: false; toolbar: false;"&gt;[TestMethod]
public void CanConstructUsingConstructorInfo()
{
    var container = new UnityContainer();

    container.RegisterType&amp;lt;ICalculator, Calculator&amp;gt;(&amp;quot;FutureValue&amp;quot;,
                            new InjectionConstructor(new ResolvedParameter&amp;lt;FutureValue&amp;gt;())
                            );

    container.RegisterType&amp;lt;ICalculator, Calculator&amp;gt;(&amp;quot;PresentValue&amp;quot;,
                            new InjectionConstructor(new ResolvedParameter&amp;lt;PresentValue&amp;gt;())
                            );

    var calc1 = container.Resolve&amp;lt;ICalculator&amp;gt;(&amp;quot;FutureValue&amp;quot;);
    var calc2 = container.Resolve&amp;lt;ICalculator&amp;gt;(&amp;quot;PresentValue&amp;quot;);
}&lt;/pre&gt;

&lt;p&gt;At this point, I need to step aside and point out that there is a potential problem with this approach.&amp;#160; We’ve created a mechanism that allows our type to be easily resolved directly from the container using it’s unique name.&amp;#160; There is temptation to pass the container into our code and resolve these objects by name.&amp;#160; This is a slippery slope that will quickly tie your application code and tests directly to Unity.&amp;#160; I will blog more about this in an upcoming post, but here are some better alternatives:&lt;/p&gt;

&lt;h5&gt;Resolved Parameters&lt;/h5&gt;

&lt;p&gt;Rather than manually resolving the named instance from the container, configure the container to use your named instance:&lt;/p&gt;

&lt;pre class="brush: js; gutter: false; toolbar: false;"&gt;public class Example
{
    public Example(ICalculator calculator)
    {
        Calculator = calculator;
    }

    public ICalculator Calculator
    {
        get;
        set;
    }
}

[TestClass]
public class ExampleTest
{
    [TestMethod]
    public void ConfigureExampleForNamedInstance()
    {
        var container = new UnityContainer();
        container.RegisterType&amp;lt;ICalculator, Calculator&amp;gt;(&amp;quot;FutureValue&amp;quot;,
                                    new InjectionConstructor(
                                            new ResolvedParameter&amp;lt;FutureValue&amp;gt;())
                                    );

        container.RegisterType&amp;lt;Example&amp;gt;(
                                    new InjectionConstructor(
                                            new ResolvedParameter&amp;lt;ICalculator&amp;gt;(&amp;quot;FutureValue&amp;quot;))
                                    );

        Example ex = container.Resolve&amp;lt;Example&amp;gt;();
    }
}&lt;/pre&gt;

&lt;h5&gt;Dependency Attributes&lt;/h5&gt;

&lt;p&gt;Unity supports special attributes that can be applied to your code to provide configuration guidance.&amp;#160; This can make instrumenting your code much easier, but it has a downside of brining the dependency injection framework into your code.&amp;#160; This isn’t necessarily the end of the world, but can limit us if we want to reuse this class with a different dependency or overload it in another container.&lt;/p&gt;

&lt;pre class="brush: js; gutter: false; toolbar: false;"&gt;public class Example
{
    public Example(
                [Dependency(&amp;quot;FutureValue&amp;quot;)]
                ICalculator calculator
                  )
    {
        Calculator = calculator;
    }

    public ICalculator Calculator
    {
        get;
        set;
    }
}&lt;/pre&gt;

&lt;p&gt;Alternatively, the DependencyAttribute can be applied to Properties as well for Setter injection.&amp;#160; Personally, I find this reads better than using attributes in the constructor, and I’ll save my points about Setter injection for another post.&lt;/p&gt;

&lt;pre class="brush: js; gutter: false; toolbar: false;"&gt;public class Example
{
    [Dependency(&amp;quot;FutureValue&amp;quot;)]
    public ICalculator Calculator
    {
        get; set;
    }
}&lt;/pre&gt;

&lt;h3&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;Although inversion of control promotes use of interfaces to decouple our implementations, we often use abstract classes and composition to construct our objects.&amp;#160; In the case of composition, we need to provide Unity with the appropriate information to construct our objects.&amp;#160; Wherever possible, we should defer construction logic to configuration settings rather than instrumenting the code with container information.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8942599-6377406566472727366?l=www.bryancook.net' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/GyyKMDKKSZcLtaptRI8mPOQ3Xkc/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/GyyKMDKKSZcLtaptRI8mPOQ3Xkc/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/GyyKMDKKSZcLtaptRI8mPOQ3Xkc/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/GyyKMDKKSZcLtaptRI8mPOQ3Xkc/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=OgFPda7ps1M:EjJZAtJEyEI:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=OgFPda7ps1M:EjJZAtJEyEI:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=OgFPda7ps1M:EjJZAtJEyEI:G79ilh31hkQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=G79ilh31hkQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=OgFPda7ps1M:EjJZAtJEyEI:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?i=OgFPda7ps1M:EjJZAtJEyEI:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bryanbcook/~4/OgFPda7ps1M" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.bryancook.net/feeds/6377406566472727366/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=8942599&amp;postID=6377406566472727366" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8942599/posts/default/6377406566472727366?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8942599/posts/default/6377406566472727366?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/bryanbcook/~3/OgFPda7ps1M/implementing-strategy-pattern-using.html" title="Implementing a Strategy Pattern using the Unity Framework" /><author><name>bryan</name><uri>http://www.blogger.com/profile/01332614158223702009</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="09074999001888279291" /></author><thr:total>1</thr:total><feedburner:origLink>http://www.bryancook.net/2009/11/implementing-strategy-pattern-using.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUcEQXw7eyp7ImA9WxNUF0o.&quot;"><id>tag:blogger.com,1999:blog-8942599.post-5486885816784660187</id><published>2009-11-09T09:30:00.000-05:00</published><updated>2009-11-09T09:30:00.203-05:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-11-09T09:30:00.203-05:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term=".net" /><title>Simple Dependency Injection using the Unity Framework</title><content type="html">&lt;p&gt;My current project is using Unity as an Inversion of Control / Dependency Injection framework.&amp;#160; We’ve had a few challenges and lessons learned while using it.&amp;#160; This is my first post on the topic, and I want to provide a quick look:&lt;/p&gt;  &lt;p&gt;Unity is slightly different than most dependency injection frameworks that I’ve seen.&amp;#160; Most dependency injection frameworks, such as Spring.NET (when I last used it), require you to explicitly define the dependencies for your objects in configuration files.&amp;#160; Unity also uses configuration files, but it also can construct any object at runtime by examining its constructor.&lt;/p&gt;  &lt;p&gt;Let’s take a simple example where I have a product catalog service that provides simple searching for products by category.&amp;#160; The service is backed by a simple and crude ProductRepository object.&lt;/p&gt;  &lt;pre class="brush: csharp; gutter: false;"&gt;public class ProductCatalog
{
    public ProductCatalog(ProductRepository repository)
    {
        _repository = repository;
    }

    public List&amp;lt;Product&amp;gt; GetProductsByCategory(string category)
    {
        return _repository.GetAll()
                          .Where(p =&amp;gt; p.Category == category)
                          .ToList();
    }

    private ProductRepository _repository;
}

public class ProductRepository
{
    public ProductRepository()
    {
        _products = new List&amp;lt;Product&amp;gt;()
            {
            new Product() { Id = &amp;quot;1&amp;quot;, Name = &amp;quot;Snuggie&amp;quot;, Category = &amp;quot;Seen on TV&amp;quot; },
            new Product() { Id = &amp;quot;2&amp;quot;, Name = &amp;quot;Slap Chop&amp;quot;, Category = &amp;quot;Seen on TV&amp;quot; }
            };
    }

    public virtual List&amp;lt;Product&amp;gt; GetAll()
    {
        return _products;
    }

    private List&amp;lt;Product&amp;gt; _products;
}

public class Product
{
    public string Id { get; set; }
    public string Name { get; set; }
    public string Category { get; set; }
}&lt;/pre&gt;

&lt;p&gt;With no configuration whatsoever, I can query Unity to resolve my ProductCatalog in a single line of code.&amp;#160; Unity will inspect the constructor of the ProductCatalog and discover that it is dependent on the ProductRepository.&amp;#160; Since the ProductRepository does not require any additional dependencies in its constructor, Unity will construct the repository and assign it to the catalog for me.&lt;/p&gt;

&lt;pre class="brush: csharp;  gutter: false;"&gt;[Test]
public void CanResolveWithoutConfiguration()
{
    var container = new Microsoft.Practices.Unity.UnityContainer();

    var catalog = container.Resolve&amp;lt;ProductCatalog&amp;gt;();

    var products = catalog.GetProductsByCategory(&amp;quot;Seen on TV&amp;quot;);

    Assert.AreEqual(2, products.Count);
}&lt;/pre&gt;

&lt;p&gt;Aside from the fact that this is a crude example, a production system would be extremely limited because the product catalog is tightly coupled to our repository implementation.&amp;#160; Although we could subclass the product repository, a better solution would decouple the two classes by introducing an interface for the product repository.&lt;/p&gt;

&lt;pre class="brush: csharp; gutter: false"&gt;public interface IProductRepository
{
    List&amp;lt;Product&amp;gt; GetAll();
}

public class ProductRepository : IProductRepository
{
    // ...
}

public class ProductCatalog
{
    public ProductCatalog(IProductRepository repository)
    {
        // ...
    }

    // ...
}&lt;/pre&gt;

&lt;p&gt;This is a much more ideal solution as our components are now loosely coupled, but Unity no longer has enough information to automatically resolve the ProductCatalog’s dependencies.&amp;#160; To fix this issue, we need to provide this information to the Unity container.&amp;#160; This can be done through configuration files, or by manipulating the container directly:&lt;/p&gt;

&lt;pre class="brush: csharp; highlight: [6];  gutter: false;"&gt;[Test]
public void CanResolveWithRegisteredType()
{
    var container = new Microsoft.Practices.Unity.UnityContainer();
    
    container.RegisterType&amp;lt;IProductRepository, ProductRepository&amp;gt;();

    var catalog = container.Resolve&amp;lt;ProductCatalog&amp;gt;();

    var products = catalog.GetProductsByCategory(&amp;quot;Seen on TV&amp;quot;);

    Assert.AreEqual(2, products.Count);
}&lt;/pre&gt;

&lt;blockquote&gt;
  &lt;p&gt;Note that while I’m using the Unity container in my tests, it's completely unnecessary. I can simply mock out my dependencies (IProductRepository), pass them in, and test normally.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;This demonstrates how Unity is able to resolve simple types without extensive configuration.&amp;#160; By designing our objects to have their dependencies passed in they possess no knowledge of the dependency injection framework, making them lean, more portable and easy to test.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8942599-5486885816784660187?l=www.bryancook.net' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/GbTRFIu2NJv6WoWmwflkNgSJ95E/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/GbTRFIu2NJv6WoWmwflkNgSJ95E/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/GbTRFIu2NJv6WoWmwflkNgSJ95E/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/GbTRFIu2NJv6WoWmwflkNgSJ95E/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=0eCbhVsJfws:dkW3qxUCt40:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=0eCbhVsJfws:dkW3qxUCt40:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=0eCbhVsJfws:dkW3qxUCt40:G79ilh31hkQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=G79ilh31hkQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=0eCbhVsJfws:dkW3qxUCt40:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?i=0eCbhVsJfws:dkW3qxUCt40:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bryanbcook/~4/0eCbhVsJfws" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.bryancook.net/feeds/5486885816784660187/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=8942599&amp;postID=5486885816784660187" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8942599/posts/default/5486885816784660187?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8942599/posts/default/5486885816784660187?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/bryanbcook/~3/0eCbhVsJfws/simple-dependency-injection-using-unity.html" title="Simple Dependency Injection using the Unity Framework" /><author><name>bryan</name><uri>http://www.blogger.com/profile/01332614158223702009</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="09074999001888279291" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.bryancook.net/2009/11/simple-dependency-injection-using-unity.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUENQXk9cCp7ImA9WxNVE0w.&quot;"><id>tag:blogger.com,1999:blog-8942599.post-2970671263093671978</id><published>2009-10-23T08:35:00.001-04:00</published><updated>2009-10-23T13:08:10.768-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-10-23T13:08:10.768-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="windows 7" /><category scheme="http://www.blogger.com/atom/ns#" term="fun" /><title>Vista: Will not be missed</title><content type="html">&lt;p&gt;&lt;a href="http://lh3.ggpht.com/_XB6P2gWz--8/SuFDXNsFwiI/AAAAAAAAAb0/LASpkIGrTPk/s1600-h/tombstone1%5B19%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; margin-left: 0px; border-top: 0px; margin-right: 0px; border-right: 0px" title="Here be the final resting place of windows vista" border="0" alt="Here be the final resting place of windows vista" src="http://lh5.ggpht.com/_XB6P2gWz--8/SuFDaQ_SrYI/AAAAAAAAAb4/n-v8gJ_tYxU/tombstone1_thumb%5B17%5D.png?imgmax=800" width="426" height="507" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Vista, born into the Windows family early 2007, was finally laid to rest earlier this week.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8942599-2970671263093671978?l=www.bryancook.net' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/dTJMt6g3oQk0xW27dYQ6Ne8Ygdg/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/dTJMt6g3oQk0xW27dYQ6Ne8Ygdg/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/dTJMt6g3oQk0xW27dYQ6Ne8Ygdg/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/dTJMt6g3oQk0xW27dYQ6Ne8Ygdg/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=m1TLmFPxTrk:-_itJ95OCAo:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=m1TLmFPxTrk:-_itJ95OCAo:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=m1TLmFPxTrk:-_itJ95OCAo:G79ilh31hkQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=G79ilh31hkQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=m1TLmFPxTrk:-_itJ95OCAo:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?i=m1TLmFPxTrk:-_itJ95OCAo:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bryanbcook/~4/m1TLmFPxTrk" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.bryancook.net/feeds/2970671263093671978/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=8942599&amp;postID=2970671263093671978" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8942599/posts/default/2970671263093671978?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8942599/posts/default/2970671263093671978?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/bryanbcook/~3/m1TLmFPxTrk/vista-will-not-be-missed.html" title="Vista: Will not be missed" /><author><name>bryan</name><uri>http://www.blogger.com/profile/01332614158223702009</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="09074999001888279291" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.bryancook.net/2009/10/vista-will-not-be-missed.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0ECSHg8fCp7ImA9WxNVEUk.&quot;"><id>tag:blogger.com,1999:blog-8942599.post-7995607546074307199</id><published>2009-10-21T00:58:00.001-04:00</published><updated>2009-10-21T12:14:29.674-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-10-21T12:14:29.674-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term=".net" /><category scheme="http://www.blogger.com/atom/ns#" term="TDD" /><title>Running MS Team System tests in NUnit</title><content type="html">&lt;p&gt;Earlier this week I saw &lt;a href="http://stackoverflow.com/questions/1564681/running-vsts-tests-without-mstest-exe" target="_blank"&gt;this question on Stack Overflow&lt;/a&gt; asking if it was possible to run MS Team System Tests under NUnit.&amp;#160; At first, this sounds like a really odd request.&amp;#160; After all, why not just convert your tests to NUnit and be done with it?&amp;#160; &lt;/p&gt;  &lt;p&gt;I can think of a few examples where you may need interoperability – for example, two teams collaborating where team #1 has corporate policy for MSTest, while team #2 doesn’t have access to a version of Visual Studio with Team System.&amp;#160; Being able to target one platform but have the tests run in either environment is desirable.&amp;#160; Whatever the reason, it’s an interesting code challenge.&lt;/p&gt;  &lt;p&gt;Fortunately, the good folks at &lt;a href="http://www.exactmagic.com/blog/2008/02/09/microsoft-team-system-nunit-adapter/" target="_blank"&gt;Exact Magic Software published an NUnit addin&lt;/a&gt; that can adapt Team System test fixtures to NUnit.&amp;#160; Since NUnit identifies candidate fixtures simply by examining attribute names, the addin’s job is fairly straight forward, but it also recognizes MS specific features such as TestContext and DataSource driven tests.&lt;/p&gt;  &lt;p&gt;Unfortunately, the NUnit adapter uses features that reside within the &lt;em&gt;nunit.core&lt;/em&gt; assembly which ties it to a specific version of NUnit.&amp;#160; In this case, the addin will only work with NUnit 2.4.6.&amp;#160; This is a frustrating design problem that plagues many NUnit addins, including my own &lt;a href="http://seleniumtoolkit.codeplex.com" target="_blank"&gt;open source project&lt;/a&gt;.&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;em&gt;Note: NUnit 3.0 plans to solve this problem by moving the boundary between framework and test runner so that each version of the framework knows how to run it’s own tests.&amp;#160; The GUI or host application will be able to load and execute the tests in a version independent manner.&amp;#160; Hopefully this means that addins will target and execute within a specific framework version, but will work in different versions of the user-interface.&lt;/em&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;The current version of NUnit (2.5.2) is a stepping stone between the 2.4.x framework and the upcoming overhauled 3.0 version.&amp;#160; As part of this transition, there’s a lot of breaking changes between older versions.&amp;#160; For me, this translates into a problem where Exact Magic’s project simply won’t compile if you update the dependencies.&amp;#160; Since I need to do similar work for my own project, this was good exercise.&lt;/p&gt;  &lt;p&gt;I’ve reworked their code and &lt;a href="http://snippetware.googlecode.com/files/ExactMagic.MstsAdapter.zip" target="_blank"&gt;put it here&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;A few known caveats:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;The unit tests for the adapter attempt to perform reflection calls to internals fields or methods within the NUnit.Core that have been renamed or no longer exist.&amp;#160; I haven’t bothered fixing these tests. &lt;/li&gt;    &lt;li&gt;Does not appear to have support for AssemblyInitialize or AssemblyCleanup, though these could be adapted by adding an EventListener when the test run start and finish.&amp;#160; I may add this feature. &lt;/li&gt;    &lt;li&gt;The adapter doesn’t wrap functionality contained within MS Test, it simulates it’s behavior.&amp;#160; While it has the same result, it won’t capture the exact same nuances of Microsoft’s implementation. &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;If you have any concerns or questions, let me know.&lt;/p&gt;  &lt;p&gt;Cheers.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8942599-7995607546074307199?l=www.bryancook.net' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/qmjIsBRbFCENw6Xy9qZFqzGEb_k/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/qmjIsBRbFCENw6Xy9qZFqzGEb_k/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/qmjIsBRbFCENw6Xy9qZFqzGEb_k/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/qmjIsBRbFCENw6Xy9qZFqzGEb_k/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=uOJzg7sCwz4:M26zKm70N7I:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=uOJzg7sCwz4:M26zKm70N7I:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=uOJzg7sCwz4:M26zKm70N7I:G79ilh31hkQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=G79ilh31hkQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=uOJzg7sCwz4:M26zKm70N7I:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?i=uOJzg7sCwz4:M26zKm70N7I:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bryanbcook/~4/uOJzg7sCwz4" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.bryancook.net/feeds/7995607546074307199/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=8942599&amp;postID=7995607546074307199" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8942599/posts/default/7995607546074307199?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8942599/posts/default/7995607546074307199?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/bryanbcook/~3/uOJzg7sCwz4/mstest-nunit-adapter.html" title="Running MS Team System tests in NUnit" /><author><name>bryan</name><uri>http://www.blogger.com/profile/01332614158223702009</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="09074999001888279291" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.bryancook.net/2009/10/mstest-nunit-adapter.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkICQX45cSp7ImA9WxNVEEk.&quot;"><id>tag:blogger.com,1999:blog-8942599.post-1099494042819967124</id><published>2009-10-20T09:16:00.000-04:00</published><updated>2009-10-20T09:16:00.029-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-10-20T09:16:00.029-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="BDD" /><category scheme="http://www.blogger.com/atom/ns#" term=".net" /><category scheme="http://www.blogger.com/atom/ns#" term="TDD" /><title>My current approach to Context/Specification</title><content type="html">&lt;p&gt;One of my most popular posts from last year is an article on &lt;a href="http://www.bryancook.net/2008/06/test-naming-conventions-guidelines.html"&gt;test naming guidelines&lt;/a&gt;, which was written to resemble the format used by the &lt;a href="http://www.amazon.ca/Framework-Design-Guidelines-Conventions-Libraries/dp/0321246756" target="_blank"&gt;Framework Design Guidelines&lt;/a&gt;.&amp;#160; Despite the popularity of the article, I started to stray from those guidelines over the last year.&amp;#160; While I haven’t abandoned the philosophy of those guidelines, in fact most of the general advise still applies, I’ve begun to adopt a much different approach for structuring and organizing my tests, and as result, the naming has changed slightly too.&lt;/p&gt;  &lt;p&gt;The syntax I’ve promoted and followed for years, let’s call it &lt;em&gt;TDD&lt;/em&gt; or &lt;em&gt;original-flavor&lt;/em&gt;, has a few known side-effects:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Unnecessary or complex Setup – You declare common setup logic in the &lt;em&gt;Setup&lt;/em&gt; of your tests, but not all tests require this initialization logic.&amp;#160; In some cases, a test requires entirely different setup logic, so the initial “arrange” portion of the test must undo some of the work done in the setup. &lt;/li&gt;    &lt;li&gt;Grouping related tests – When a complex component has a lot of tests that handle different scenarios, keeping dozens of tests organized can be difficult.&amp;#160; Naming conventions can help here, but are masking the underlying problem. &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;Over the last year, I’ve been experimenting with a Behavior-Driven-Development flavor of tests, often referred to as Context/Specification pattern.&amp;#160; While it addresses the side-effects outlined above, the goal of “true” BDD is to attempt to describe requirements in a common language for technical and non-technical members of an Agile project, often with a Given / When / Should syntax.&amp;#160; For example,&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;em&gt;&lt;strong&gt;Given&lt;/strong&gt; a bank account in good standing, &lt;/em&gt;&lt;em&gt;&lt;strong&gt;when&lt;/strong&gt; the customer requests cash, &lt;/em&gt;&lt;em&gt;the bank account &lt;strong&gt;should &lt;/strong&gt;be debited&lt;/em&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;The underlying concept is when tests are written using this syntax, they become executable specifications – and that’s really cool.&amp;#160; Unfortunately, I’ve always considered this syntax to be somewhat awkward, and how I’ve adopted this approach is a rather loose interpretation.&amp;#160; I’m also still experimenting, so your feedback is definitely welcome.&lt;/p&gt;  &lt;h3&gt;An Example&lt;/h3&gt;  &lt;p&gt;Rather than try to explain the concepts and the coding style in abstract terms, I think it’s best to let the code speak for itself first and then try reason my way out.&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;em&gt;Note: I’ve borrowed and bended concepts from many different sources, some I can’t recall where.&amp;#160; This example borrows many concepts from Scott Bellware’s &lt;a href="http://code.google.com/p/specunit-net/"&gt;specunit-net&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;pre class="brush: csharp;"&gt;public abstract class ContextSpecification
{
    [TestFixtureSetUp]
    public void SetupFixture()
    {
       BeforeAllSpecs();
    }
    
    [TestFixtureTearDown]
    public void TearDownFixture()
    {
       AfterAllSpecs();
    }

     [SetUp]
     public void Setup()
    {
       Context();
       Because();
    }

    [TearDown]
    public void TearDown()
    {
       CleanUp();
    }

    protected virtual void BeforeAllSpecs() { }
    protected virtual void Context() { }
    protected virtual void Because() { }
    protected virtual void CleanUp() { }
    protected virtual void AfterAllSpecs() { }   
}

public class ArgumentBuilderSpecs : ContextSpecification
{
    protected ArgumentBuilder builder;
    protected Dictionary&amp;lt;string,string&amp;gt; settings;
    protected string results;

    protected override void Context()
    {
       builder = new ArgumentBuilder();
       settings = new Dictionary&amp;lt;string,string&amp;gt;();
    }

    protected override void Because()
    { 
       results = builder.Parse(settings);
    }

    [TestFixture]
    public class WhenNoArgumentsAreSupplied : ArgumentBuilderSpecs
    {
       [Test]
       public void ResultsShouldBeEmpty()
       {
           results.ShouldBeEmpty();
       }
    }

    [TestFixture]
    public class WhenProxyServerSettingsAreSupplied : ArgumentBuilderSpecs
    {
       protected override void Because()
       {
           settings.Add(&amp;quot;server&amp;quot;, &amp;quot;proxyServer&amp;quot;);
           settings.Add(&amp;quot;port&amp;quot;, &amp;quot;8080&amp;quot;);

           base.Because();
       }

       [Test]
       public void ShouldContainProxyServerArgument()
       {
           results.ShouldContain(&amp;quot;-DhttpProxy:proxyServer&amp;quot;);
       }

       [Test]
       public void ShouldContainProxyPortArgument()
       {
           results.ShouldContain(&amp;quot;-DhttpPort:8080&amp;quot;);
       }
  }
}&lt;/pre&gt;

&lt;p&gt;Compared to my original flavor, this new bouquet has some considerable differences which may seem odd to an adjusted palate.&amp;#160; Let’s walk through those differences:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;No longer using &lt;em&gt;Fixture-per-Class&lt;/em&gt; structure, where all tests for a class reside within a single class. &lt;/li&gt;

  &lt;li&gt;Top level “specification” &lt;em&gt;ArgumentBuilderSpecs&lt;/em&gt; is not decorated with a &lt;em&gt;[TestFixture]&lt;/em&gt; attribute, nor does it contain any tests. &lt;/li&gt;

  &lt;li&gt;&lt;em&gt;ArgumentBuilderSpecs&lt;/em&gt; derives from a base class &lt;em&gt;ContextSpecification&lt;/em&gt; which controls the setup/teardown logic and the semantic structure of the BDD syntax. &lt;/li&gt;

  &lt;li&gt;&lt;em&gt;ArgumentBuilderSpecs&lt;/em&gt; contains the variables that are common to all tests, but setup logic is kept to a minimum. &lt;/li&gt;

  &lt;li&gt;ArgumentBuilderSpecs contains two nested classes that derive from ArgumentBuilderSpecs.&amp;#160; Each nested class is a test-fixture for a scenario or context. &lt;/li&gt;

  &lt;li&gt;Each Test-Fixture focuses on a single action only and is responsible for its own setup. &lt;/li&gt;

  &lt;li&gt;Each Test represents a single specification, often only as a single Assert. &lt;/li&gt;

  &lt;li&gt;Asserts are made using Extension methods (not depicted in the code example) &lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Observations&lt;/h3&gt;

&lt;h4&gt;Inheritance&lt;/h4&gt;

&lt;p&gt;I’ve never been a big fan of using inheritance, especially in test scenarios as it requires more effort on part of the future developer to understand the test structure.&amp;#160; In this example, inheritance plays a part in both the base class and nested classes, though you could argue the impact of inheritance is negated since the base class only provides structure, and the derived classes are clearly visible within the parent class.&amp;#160; It’s a bit unwieldy, but the payoff for using inheritance is found when viewing the tests in their hierarchy:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://lh5.ggpht.com/_XB6P2gWz--8/St0zlPs39FI/AAAAAAAAAbs/YSTnpgxaz_M/s1600-h/ContextSpecExample3.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="Context-Spec-Example" border="0" alt="Context-Spec-Example" src="http://lh4.ggpht.com/_XB6P2gWz--8/St0zmbm1X8I/AAAAAAAAAbw/Kgnp0PKOS1M/ContextSpecExample_thumb1.png?imgmax=800" width="644" height="266" /&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;While technically you could achieve a similar effect by using namespaces to group tests, but you lose some of the benefits of encapsulation of test-helper methods and common variables.&lt;/p&gt;

&lt;p&gt;Although we can extend our contexts by deriving from the parent class, this approach is limited to inheriting from the root specification container (&lt;em&gt;ArgumentBuilderSpecs&lt;/em&gt;).&amp;#160; If you were to derive from &lt;em&gt;WhenProxyServerSettingsAreSupplied&lt;/em&gt; for example, you would inherit the test cases from that class as well.&amp;#160; I have yet to find a scenario where I needed to do this.&amp;#160; While the concepts of &lt;a href="http://en.wikipedia.org/wiki/Don't_repeat_yourself" target="_blank"&gt;DRY&lt;/a&gt; make sense, there’s a lot to be said about clear intent of test cases where duplication aids readability.&lt;/p&gt;

&lt;h4&gt;Extra Plumbing&lt;/h4&gt;

&lt;p&gt;There’s quite a bit of extra plumbing to be able to create our nested contexts, and it seems to take a bit longer to write tests.&amp;#160; This delay is either caused by grappling with new context/specification concepts, writing additional code for subclasses or more thought determining which contexts are required.&amp;#160; I’m anticipating that it gets easier with more practice, and some Visual Studio code snippets might simplify the authoring process.&lt;/p&gt;

&lt;p&gt;One area where I can sense I’m slowing down is trying to determine if I should be overriding or extending &lt;em&gt;Context&lt;/em&gt; versus &lt;em&gt;Because.&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;Granular Tests&lt;/h4&gt;

&lt;p&gt;In this style of tests, where a class is created to represent a context, each context performs only one small piece of work and the tests serve as assertions against the outcome.&amp;#160; I found that the tests I wrote were concise and elegant, and the use of classes to structure the tests around the context helped to organize both the tests and the responsibility of the subject under test.&amp;#160; I also found that I wrote fewer tests with this approach – I write only a few principle contexts.&amp;#160; If a class starts to have too many contexts, it could mean that the class has too much responsibility and should be split into smaller parts. &lt;/p&gt;

&lt;p&gt;Regarding the structure of the tests, if you’re used to fixture-per-class style of tests, it may take some time to get accustomed to the context performing only a single action.&amp;#160; Though as &lt;a href="http://stevenharman.net/blog/archive/2009/05/27/toward-a-better-use-of-context-specification.aspx" target="_blank"&gt;Steven Harman points out&lt;/a&gt;, visualizing the context setup as part of the fixture setup may help guide your transition to this style of testing.&lt;/p&gt;

&lt;h3&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;I’m enjoying writing Context/Specification style tests as they read like a specification document and provide clear feedback of what the system does.&amp;#160; In addition, when a test fails, the context it belongs to provides additional meaning around the failure.&lt;/p&gt;

&lt;p&gt;There appear to be many different formats for writing tests in this style, and my current format is a work in progress.&amp;#160; Let me know what you think.&lt;/p&gt;

&lt;p&gt;Presently, I’m flip flopping between this format and my old habits.&amp;#160; I’ve caught myself a few times where I write a flat structure of tests without taking context into consideration – after a point, the number of tests becomes unmanageable, and it becomes difficult to identify if I should be tweaking existing tests or writing new ones.&amp;#160; If the tests were organized by context, the number of tests becomes irrelevant, and the focus is placed on the scenarios that are being tested.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8942599-1099494042819967124?l=www.bryancook.net' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/UqepIEtTuK0hJVDT_8eqMZIeWBM/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/UqepIEtTuK0hJVDT_8eqMZIeWBM/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/UqepIEtTuK0hJVDT_8eqMZIeWBM/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/UqepIEtTuK0hJVDT_8eqMZIeWBM/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=7VO2HBiHBUQ:QNiW4c-mfmw:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=7VO2HBiHBUQ:QNiW4c-mfmw:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=7VO2HBiHBUQ:QNiW4c-mfmw:G79ilh31hkQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=G79ilh31hkQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=7VO2HBiHBUQ:QNiW4c-mfmw:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?i=7VO2HBiHBUQ:QNiW4c-mfmw:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bryanbcook/~4/7VO2HBiHBUQ" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.bryancook.net/feeds/1099494042819967124/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=8942599&amp;postID=1099494042819967124" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8942599/posts/default/1099494042819967124?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8942599/posts/default/1099494042819967124?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/bryanbcook/~3/7VO2HBiHBUQ/my-current-approach-to.html" title="My current approach to Context/Specification" /><author><name>bryan</name><uri>http://www.blogger.com/profile/01332614158223702009</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="09074999001888279291" /></author><thr:total>1</thr:total><feedburner:origLink>http://www.bryancook.net/2009/10/my-current-approach-to.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEQEQXw5eip7ImA9WxNWGUg.&quot;"><id>tag:blogger.com,1999:blog-8942599.post-9009258741205274270</id><published>2009-10-19T08:45:00.000-04:00</published><updated>2009-10-19T08:45:00.222-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-10-19T08:45:00.222-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="selenium toolkit" /><category scheme="http://www.blogger.com/atom/ns#" term=".net" /><category scheme="http://www.blogger.com/atom/ns#" term="selenium" /><title>Configuring the Selenium Toolkit for Different Environments</title><content type="html">&lt;p&gt;Suppose you’ve written some selenium tests using Selenium IDE that target your local machine &lt;em&gt;(http://localhost)&lt;/em&gt;, but you’d like to repurpose the same tests for your QA, Staging or Production environment as part of your build process.&amp;#160; The &lt;a href="http://seleniumtoolkit.codeplex.com/"&gt;Selenium Toolkit for .NET&lt;/a&gt; makes this really easy to change the target environment without having to touch your tests. &lt;/p&gt;  &lt;p&gt;In this post, I’ll show you how to configure the Selenium Toolkit for the following scenarios:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Local Selenium RC / Local Web Server &lt;/li&gt;    &lt;li&gt;Local Selenium RC / Remote Web Server &lt;/li&gt;    &lt;li&gt;Remote Selenium RC / Remote Web Server &lt;/li&gt; &lt;/ul&gt;  &lt;h3&gt;Running Locally&lt;/h3&gt;  &lt;p&gt;In this scenario, both the Selenium Remote Control Host (aka Selenium RC / Selenium Server) and the web site you want to test are running on your local machine.&amp;#160; This is perhaps the most common scenario for developers who develop against a local environment, though it also applies to your build server if the web server you want to test is also on that machine.&amp;#160; &lt;/p&gt;  &lt;p&gt;Although this configuration is limited to running tests against the current operating system and installed browsers, it provides a few advantages to developers as they can watch the tests execute or set breakpoints on the server to assist in debugging.&lt;/p&gt;  &lt;p&gt;This is the default configuration for the Selenium Toolkit for .NET – when the tests execute, the NUnit Addin will automatically start the Selenium RC process and shut it down when the tests complete.&lt;/p&gt;  &lt;p&gt;Assuming you have installed the Selenium Toolkit for .NET, the only configuration setting you'll need to provide is the URL of the web site you want to test. In this example, we assume that &lt;em&gt;http://mywebsite&lt;/em&gt; is running on the local web server. &lt;/p&gt;  &lt;pre class="brush: xml; gutter: false;"&gt;&amp;lt;Selenium BrowserUrl=&amp;quot;http://mywebsite&amp;quot; /&amp;gt;&lt;/pre&gt;

&lt;h3&gt;Running Tests against a Remote Web Server&lt;/h3&gt;

&lt;p&gt;In this scenario Selenium RC runs local, but web server is on a remote machine.&amp;#160; You’ll still see the browser executing the tests locally, but because the web-server is physically located elsewhere it’s not as easy to debug server-side issues.&amp;#160; From a configuration perspective, this scenario uses the same configuration settings as above, except the URL of the server is not local.&lt;/p&gt;

&lt;p&gt;This scenario is typically used in a build server environment.&amp;#160; For example, the build server compiles and deploys the web application to a target machine using rsync and then uses Selenium to validate the deployment using a few functional tests.&lt;/p&gt;

&lt;h3&gt;Executing Tests in a Remote Environment / Selenium Grid&lt;/h3&gt;

&lt;p&gt;In this scenario your local test-engine executes your tests against a remote Selenium RC process.&amp;#160; While this could be a server where the selenium RC process is configured to run as a dedicated service, it’s more likely that you would use this configuration for executing your tests against a Selenium Grid.&amp;#160; The Selenium Grid server exposes the same API as the Selenium RC, but it acts as a broker between your tests and multiple operating systems and different browser configurations.&lt;/p&gt;

&lt;p&gt;To configure the Selenium Toolkit to use a Selenium Grid, you’ll need to specify the location of the Grid Server and turn off the automatic start/stop feature:&lt;/p&gt;

&lt;pre class="brush: xml; gutter: false;"&gt;&amp;lt;Selenium
     server=&amp;quot;grid-server-name&amp;quot;
     port=&amp;quot;4444&amp;quot;  
     BrowserUrl=&amp;quot;http://mywebsite.com&amp;quot;
   /&amp;gt;
   &amp;lt;runtime
        autoStart=&amp;quot;false&amp;quot;
        /&amp;gt;
&amp;lt;/Selenium&amp;gt;&lt;/pre&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8942599-9009258741205274270?l=www.bryancook.net' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/YbVbJnoLFi74jgg02gAtrRBly8w/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/YbVbJnoLFi74jgg02gAtrRBly8w/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/YbVbJnoLFi74jgg02gAtrRBly8w/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/YbVbJnoLFi74jgg02gAtrRBly8w/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=HWu59I-VLL4:TvzriJPl-mQ:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=HWu59I-VLL4:TvzriJPl-mQ:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=HWu59I-VLL4:TvzriJPl-mQ:G79ilh31hkQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=G79ilh31hkQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=HWu59I-VLL4:TvzriJPl-mQ:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?i=HWu59I-VLL4:TvzriJPl-mQ:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bryanbcook/~4/HWu59I-VLL4" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.bryancook.net/feeds/9009258741205274270/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=8942599&amp;postID=9009258741205274270" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8942599/posts/default/9009258741205274270?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8942599/posts/default/9009258741205274270?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/bryanbcook/~3/HWu59I-VLL4/configuring-selenium-toolkit-for.html" title="Configuring the Selenium Toolkit for Different Environments" /><author><name>bryan</name><uri>http://www.blogger.com/profile/01332614158223702009</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="09074999001888279291" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.bryancook.net/2009/10/configuring-selenium-toolkit-for.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0UCQXo_eSp7ImA9WxNWFk0.&quot;"><id>tag:blogger.com,1999:blog-8942599.post-4547523810623804066</id><published>2009-10-15T08:21:00.000-04:00</published><updated>2009-10-15T08:21:00.441-04:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-10-15T08:21:00.441-04:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term=".net" /><title>A Proposal for Functional Testing</title><content type="html">&lt;p&gt;When I’m writing code, my preference is to follow test-driven development techniques where I’m writing tests as I go.&amp;#160; Ideally, each test fixture focuses attention on one object at a time, isolating its behavior from its dependencies.&lt;/p&gt;  &lt;p&gt;While unit tests provide us with immediate feedback about our progress, it would be foolish to deploy a system without performing some form of integration test to ensure that the systems’ components work as expected when pieced together.&amp;#160; Often, integration tests focus on a cohesive set of objects in a controlled environment, such as a restoring a database after the test.&lt;/p&gt;  &lt;p&gt;Eventually, you’ll need to bring all the components together and test them in real-world scenarios.&amp;#160; The best place to bring all these components together is the live system or staged equivalent.&amp;#160; The best tool for the job is a human inspecting the system. &lt;em&gt;Period.&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;&lt;em&gt;Wait, I thought this was supposed to be about functional testing?&lt;/em&gt;&amp;#160; Don’t worry, it is.&lt;/p&gt;  &lt;p&gt;Humans may be the best tool for the job, but if you consider the amount of effort associated with code-freezes, build-reports, packaging and deployment, verification, coordination with the client and waiting testing teams, it can be really expensive to use humans for testing.&amp;#160; This is especially true if you deliver a failed build to your testing team -- your testing team who’ve been queued up are now unable test, and must wait for the next build.&amp;#160; If you were to total up all the hours from the entire team, you’d be losing at least a day or more in scheduled cost.&lt;/p&gt;  &lt;p&gt;Functional tests can help prevent this loss in production.&lt;/p&gt;  &lt;p&gt;In addition, humans possess an understanding of what the system should do, as well as what previous versions did.&amp;#160; Humans are users of the system and can contribute greatly to the overall quality of the product.&amp;#160; However, once a human has tested and validated a feature, revisiting these tests in subsequent builds becomes more of a check than a test.&amp;#160; Having to go back and check these features becomes increasingly difficult to accomplish in short-timelines as the complexity of the system grows.&amp;#160; Invariably, shortcuts are taken, features are missed and subtle, aggravating bugs silently sneak into the system.&amp;#160; While separation of concerns and good unit tests can downplay the need for full regression tests, the value of system-wide integration tests for repetitive tasks shouldn’t be discounted.&lt;/p&gt;  &lt;p&gt;Functional tests can help here too, but these are largely the fruits of labor of my first point about validating builds.&amp;#160; Most organizations can’t capitalize on this simply because they haven’t got the base to build up from.&lt;/p&gt;  &lt;p&gt;Functional tests take a lot of criticism, however.&amp;#160; Let’s address some common (mis)beliefs.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Duplication of testing efforts / Diminishing returns.&lt;/strong&gt;&amp;#160; Where teams have invested in test driven development, tests tend to focus on the backend code artifacts as these parts are the core logic of the application.&amp;#160; Using mocks and stubs, the core logic can be tested extremely well from the database layer and up, but as unit-tests cross the boundary from controller-logic into the user-interface layer, testing becomes harder to simulate: web-applications need to concern themselves with server requests; desktop applications have to worry about things like screen resolution, user input and modal dialogs.&amp;#160; In such team environments, testing the user-interface isn’t an attractive option since most bugs, if any, originate from the core logic that can be covered by more unit tests.&amp;#160; From this perspective, adding functional tests wouldn’t provide enough insight to outweigh the effort involved.&lt;/p&gt;  &lt;p&gt;I’d agree with this perspective, if the functional tests were trying to aggressively interrogate the system at the same level of detail of their backend equivalents.&amp;#160; Unlike unit tests, functional tests are focused on emulating what the user does and sees, not on the technical aspects under the hood.&amp;#160; They operate in the live system, providing a comprehensive view of the system that is unit tests cannot.&amp;#160; In the majority of cases, a few simple tests that follow the happy path of the application may be all you need to validate the build.&lt;/p&gt;  &lt;p&gt;More over, failures at this level point to problems in the build, packaging or deployment – something well beyond a typical unit test’s reach.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Functional tests are too much effort / No time for tests&lt;/strong&gt;.&amp;#160; This is a common view that is applied to testing in general, which is based on a flawed assumption that testing should follow after development work is done.&amp;#160; In this argument, testing is seen as “double the effort”, which is an unfair position if you think about it.&amp;#160; If you treat testing as a separate task and wait until the components are fully written, then without a doubt the action of writing tests becomes an exercise in reverse-engineering and will always be more effort.&lt;/p&gt;  &lt;p&gt;Functional tests, like unit tests, should be brought into the development process.&amp;#160; While there is some investment required to get your user-interface and its components into a test-harness, the effort to add new components and tests (should) become an incremental task.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Functional tests are too brittle / Too much maintenance&lt;/strong&gt;.&amp;#160; Without doubt, the user-interface can be the most volatile part of your application, as it is subject to frequent cosmetic changes.&amp;#160; If you’re writing tests that depend on the contract of the user-interface, it shouldn’t be a surprise that they’re going to be impacted when that interface changes.&amp;#160; Claiming that your tests are the source of extra effort because of changes you introduced is an indication of a problem in your approach to testing.&lt;/p&gt;  &lt;p&gt;Rather than reacting to changes, anticipate them: if you have a change to make, use the tests to introduce that change.&amp;#160; There are many techniques to accomplish this (and I may have to blog about that later), but here’s an example: to identify tests that are impacted by your change, try removing the part that needs to change and watch which tests fail.&amp;#160; Find a test that resembles your new requirement and augment it to reflect the new requirements. The tests will fail at first, but as you add the new requirements, they’ll slowly turn green.&lt;/p&gt;  &lt;p&gt;As an added bonus, when you debug your code through the automated test, you won’t have to endure repetitive user-actions and keystrokes.&amp;#160; (Who has time for all the clicking??)&lt;/p&gt;  &lt;p&gt;This approach works well in agile environments where stories are focused on adding or changing features for an interaction and changes to the user-interface are expected.&lt;/p&gt;  &lt;h3&gt;Adding Functional Testing to your Regime&lt;/h3&gt;  &lt;h4&gt;Develop a test harness&lt;/h4&gt;  &lt;p&gt;The first step to adding functional testing into your project is the development of a test-harness that can launch the application and get it into a ready state for your tests.&amp;#160; Depending on the complexity of your application and the extent of how far you want to take your functional tests, this can seem like the largest part.&amp;#160; Fortunately most test automation products provide a “recorder” application that can generate code from user activity, which can jump start this process.&amp;#160; While these tools make it easy to get started, they are really only suitable for basic scenarios or for initial prototyping.&amp;#160; As your system evolves you quickly find that the duplication in these scripts becomes a maintenance nightmare.&lt;/p&gt;  &lt;p&gt;To avoid this issue, you’ll want to model the screens and functional behavior of your application into modular, components that hide the implementation details of the recorder tools’ output.&amp;#160; This approach shields you from having to re-record your tests and makes it easier to apply changes to tests.&amp;#160; The downfall to this approach is that it may take some deep thinking on how to model your application, and it will seem as though you’re writing a lot of code to emulate what your backend code already does.&amp;#160; However, once this initial framework is in place, it becomes easier to add new components.&amp;#160; Eventually, you reach a happy place where you can write new tests without having to record anything.&lt;/p&gt;  &lt;p&gt;The following example illustrates how the implementation details of a product editor are hidden from the test, but the user actions are clearly visible:&lt;/p&gt;  &lt;pre class="brush: csharp;"&gt;[Test]
public void CanOpenAnExistingProduct()
{
    using (var app = new App())
    {
        app.Login(&amp;quot;user1&amp;quot;, &amp;quot;p@ssw3rd&amp;quot;);

        var product = new Product()
                            {
                                Id = 1,
                                Name = &amp;quot;Foo&amp;quot;
                            };

        // opens the product editor, 
        // fills it with my values
        // saves it, closes it.
        app.CreateNewProduct(product);

        // open the dialog, find the item
        ProductEditorComponent editor = app.OpenProductEditor(&amp;quot;Foo&amp;quot;);

        // retrieves the settings of the product from the screen
        Product actual = editor.GetEntity();

        Assert.AreEqual(product, actual);
    }
}&lt;/pre&gt;

&lt;h4&gt;Write Functional Unit Tests for Screen Components&lt;/h4&gt;

&lt;p&gt;Once you’ve got a basic test-harness, you should consider developing simple functional tests for user-interface components as you add them to your application.&amp;#160;&amp;#160; If you can demo it to the client, you're probably ready to start writing functional tests. A few notes to consider at this stage:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Be pragmatic!&amp;#160; Screen components that are required as part of base use cases will have more functional tests than non-essential components. &lt;/li&gt;

  &lt;li&gt;Consider pairing developers with testers.&amp;#160; As the developer builds the UI, the tester writes the automation tests that verify the UI’s functionality.&amp;#160; Testers may guide the development of the UI to include automation ids, which reduces the amount of reverse-engineering. &lt;/li&gt;

  &lt;li&gt;Write tests as new features or changes are introduced.&amp;#160; No need to get too granular, just verify that the essentials. &lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;Verify Build Process with Functional Sanity Tests&lt;/h4&gt;

&lt;p&gt;While your functional unit tests concentrate on the behaviors of individual screen components, you’ll want to augment your build process with tests that demonstrate common user-stories that can be used to validate the build.&amp;#160; These tests mimic the minimum happy path.&lt;/p&gt;

&lt;p&gt;If you’re already using a continuous integration server to run unit tests as part of each build, functional tests can be included at this stage but can be regulated to nightly builds or as part of the release process to your quality assurance team.&lt;/p&gt;

&lt;h4&gt;Augment QA Process&lt;/h4&gt;

&lt;p&gt;As noted above, humans are a critical part of the testing of our applications and that’s not likely to change.&amp;#160; However, the framework that we used to validate the build can be reused by your testing team to write automation tests for their test cases.&amp;#160; Ideally, humans verify the stories manually, then write automation tests to represent regression tests.&lt;/p&gt;

&lt;p&gt;Tests that require repetitive or complex time consuming procedures are ideal candidates for automation.&lt;/p&gt;

&lt;h3&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;Automated functional testing can add value to your project for build verification and regression testing.&amp;#160; Being pragmatic about the components you automate and vigilant in your development process to ensure the tests remain in sync are the keys to their success.&lt;/p&gt;

&lt;p&gt;How does your organization use functional testing?&amp;#160; Where does it work work?&amp;#160; What’s your story? &lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8942599-4547523810623804066?l=www.bryancook.net' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/jWklbsd4ophaHhey5hoQrK2krA8/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/jWklbsd4ophaHhey5hoQrK2krA8/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/jWklbsd4ophaHhey5hoQrK2krA8/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/jWklbsd4ophaHhey5hoQrK2krA8/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=u2W5suMQ-dk:2YiBqqGkNlw:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=u2W5suMQ-dk:2YiBqqGkNlw:7Q72WNTAKBA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=7Q72WNTAKBA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=u2W5suMQ-dk:2YiBqqGkNlw:G79ilh31hkQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?d=G79ilh31hkQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/bryanbcook?a=u2W5suMQ-dk:2YiBqqGkNlw:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/bryanbcook?i=u2W5suMQ-dk:2YiBqqGkNlw:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/bryanbcook/~4/u2W5suMQ-dk" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.bryancook.net/feeds/4547523810623804066/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=8942599&amp;postID=4547523810623804066" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/8942599/posts/default/4547523810623804066?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/8942599/posts/default/4547523810623804066?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/bryanbcook/~3/u2W5suMQ-dk/proposal-for-functional-testing.html" title="A Proposal for Functional Testing" /><author><name>bryan</name><uri>http://www.blogger.com/profile/01332614158223702009</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="09074999001888279291" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.bryancook.net/2009/10/proposal-for-functional-testing.html</feedburner:origLink></entry></feed>
