<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-13321238</id><updated>2012-01-27T20:06:45.991+11:00</updated><category term='AOP'/><category term='alt.net'/><category term='tools'/><category term='pdc'/><category term='workflow'/><category term='silverlight'/><category term='books'/><category term='ajax'/><category term='TFS'/><category term='development'/><category term='holiday'/><category term='Sandcastle'/><category term='alm'/><category term='community'/><category term='WP7'/><category term='how to'/><category term='games'/><category term='live writer'/><category term='readify'/><category term='communication'/><category term='open source'/><category term='general'/><category term='CruiseControl.NET'/><category term='VSTS'/><category term='blogger'/><category term='scrum'/><category term='agile'/><category term='tech industry'/><category term='powershell'/><category term='mocking'/><category term='tech.ed'/><category term='microsoft'/><category term='design'/><category term='.net'/><category term='podcasting'/><category term='code contracts'/><category term='testing'/><category term='architecture'/><category term='blogging'/><category term='basics'/><category term='usability'/><category term='nhibernate'/><category term='management'/><category term='database'/><category term='presentations'/><title type='text'>Richard Banks - Agile and .NET</title><subtitle type='html'>Agile Development in a .NET world</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://www.richard-banks.org/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default?start-index=101&amp;max-results=100'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>555</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-13321238.post-5103362901708355462</id><published>2011-11-24T10:39:00.001+11:00</published><updated>2011-11-24T10:39:46.036+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='development'/><title type='text'>Parameters: You’re Doing It Wrong!</title><content type='html'>&lt;p&gt;Having parameters for a method is perfectly fine however like anything, they can be used for evil. So let me give you a tip: If your code looks anything like this method signature (and I kid you not, this is a real method) then YOU”RE DOING IT WRONG!&lt;/p&gt;  &lt;p&gt;SaveContentSetItem(ContentSetItem,String,String,Int32,Int32,Int32,Int32,DateTime,DateTime,DateTime,DateTime,   &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; DateTime,DateTime,DateTime ,DateTime,Boolean,Boolean,Boolean,Int32,Int32,Int32,Int32,Int32,Int32 ,Boolean,    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; Boolean,Boolean,Boolean,Boolean,Single,Boolean,Boolean ,Boolean,Boolean,Boolean,Boolean,Boolean,Boolean,    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; Boolean,Boolean ,Boolean,Boolean,Boolean,Boolean,Boolean,Boolean,Boolean,Boolean,    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; FileLocation,String,Stream,String,FileDisplayFormat,Boolean,Stream)&lt;/p&gt;  &lt;p&gt;Please, for the love of all things good, turn off your computer right now. Pack it in a box.&amp;#160; Put the box in a locked safe.&amp;#160; Put the safe in a bunker under a mountain. Seal the bunker using 40 foot thick concrete and collapse the entrance.&amp;#160; Place a minefield and barbed wire around the bunker, and never EVER WRITE A LINE OF CODE AGAIN!&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-5103362901708355462?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/5103362901708355462/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2011/11/parameters-youre-doing-it-wrong.html#comment-form' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/5103362901708355462'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/5103362901708355462'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2011/11/parameters-youre-doing-it-wrong.html' title='Parameters: You’re Doing It Wrong!'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-8386122928873489761</id><published>2011-11-23T11:01:00.001+11:00</published><updated>2011-11-23T11:01:15.043+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='books'/><title type='text'>Ready Player One</title><content type='html'>&lt;p&gt;&lt;a href="http://lh3.ggpht.com/-WKebYzjQDGo/Tsw3wEmakjI/AAAAAAAABPE/sIzu-kxW4xU/s1600-h/image%25255B3%25255D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: 8px 10px 0px 0px; padding-left: 0px; padding-right: 0px; display: inline; float: left; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" align="left" src="http://lh3.ggpht.com/-alU4DpuKbIM/Tsw3yIbeRkI/AAAAAAAABPM/EVs81C-wQf8/image_thumb%25255B1%25255D.png?imgmax=800" width="158" height="240" /&gt;&lt;/a&gt;I wouldn’t normally mention a fiction book here as your taste for books is likely to be different to mine, however in this case I’ll make an exception.&amp;#160; I’ve just finished reading “&lt;a href="http://www.amazon.com/Ready-Player-One-ebook/dp/B004J4WKUQ/ref=tmm_kin_title_0?ie=UTF8&amp;amp;m=AG56TWVU5XWC2&amp;amp;qid=1321957674&amp;amp;sr=8-1"&gt;Ready Player One&lt;/a&gt;” and enjoyed it so much I read it in just 2 days whilst catching planes and trains and by skipping on sleep.&amp;#160; It was such a good read I simply couldn’t put it down!&amp;#160; I loved it!&lt;/p&gt;  &lt;p&gt;Why so? Because it’s a near future, sci-fi book with a big virtual reality/gaming element that I think most geeks will love, especially those who know what an easter-egg is and those with a good knowledge of 80’s geek-, gaming- and/or pop-culture.&amp;#160; Plus it’s got some seriously fun puzzles to try and solve before the reveal happens.&lt;/p&gt;  &lt;p&gt;This book was also Amazon’s book of the month for August 2011, and deservedly so.&amp;#160; Here’s the Amazon review quoted verbatim:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;a href="http://www.amazon.com/gp/feature.html?docId=1000706551"&gt;&lt;strong&gt;Amazon Best Books of the Month, August 2011:&lt;/strong&gt;&lt;/a&gt; &lt;em&gt;Ready Player One&lt;/em&gt; takes place in the not-so-distant future--the world has turned into a very bleak place, but luckily there is OASIS, a virtual reality world that is a vast online utopia. People can plug into OASIS to play, go to school, earn money, and even meet other people (or at least they can meet their avatars), and for protagonist Wade Watts it certainly beats passing the time in his grim, poverty-stricken real life. Along with millions of other world-wide citizens, Wade dreams of finding three keys left behind by James Halliday, the now-deceased creator of OASIS and the richest man to have ever lived. The keys are rumored to be hidden inside OASIS, and whoever finds them will inherit Halliday’s fortune. But Halliday has not made it easy. And there are real dangers in this virtual world. Stuffed to the gills with action, puzzles, nerdy romance, and 80s nostalgia, this high energy cyber-quest will make geeks everywhere feel like they were separated at birth from author Ernest Cline.&lt;em&gt;--Chris Schluep&lt;/em&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;That review made it sound a little formulaic and it could easily have fallen into that trap, but blissfully it doesn’t.&amp;#160; It’s a fast paced, highly entertaining, well written book and my inner geek loved each and every bit of it, especially the retro 80’s references; from &lt;a href="http://www.clubdevo.com/"&gt;Devo&lt;/a&gt; to &lt;a href="http://www.acdc.com/"&gt;AC/DC&lt;/a&gt;, &lt;a href="http://www.wizards.com/DND/"&gt;Dungeons and Dragons&lt;/a&gt; to &lt;a href="http://en.wikipedia.org/wiki/Car_Wars"&gt;Car Wars&lt;/a&gt; (awesome!) and from &lt;a href="http://www.imdb.com/title/tt0091042/"&gt;Ferris Bueller&lt;/a&gt; to &lt;a href="http://www.imdb.com/title/tt0083413/"&gt;Family Ties&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;As a bonus, this is Ernest Cline’s debut novel.&amp;#160; If he keeps up this level of writing or, even better, improves then I can’t wait to see what he comes up with next!&lt;/p&gt;      &lt;p&gt;P.S. In the interests of full disclosure I’ve got no affiliations with the book or author and I get no kickbacks for mentioning this book, though I wouldn’t mind if there were some, hint, hint!&lt;/p&gt;  &lt;p&gt;If you haven’t read it, go grab it (just a suggestion) and if you have, did you enjoy as much as I did?&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-8386122928873489761?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/8386122928873489761/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2011/11/i-wouldnt-normally-mention-fiction-book.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/8386122928873489761'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/8386122928873489761'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2011/11/i-wouldnt-normally-mention-fiction-book.html' title='Ready Player One'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/-alU4DpuKbIM/Tsw3yIbeRkI/AAAAAAAABPM/EVs81C-wQf8/s72-c/image_thumb%25255B1%25255D.png?imgmax=800' height='72' width='72'/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-3324292553191835452</id><published>2011-11-11T08:49:00.001+11:00</published><updated>2011-11-11T08:49:54.395+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='open source'/><category scheme='http://www.blogger.com/atom/ns#' term='.net'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><title type='text'>Underscores in Test Names are a Pain to Type, Right? Not Anymore!</title><content type='html'>&lt;p&gt;It might be a large assumption given some of the customers I deal with, but I’m going to assume you write unit tests.&amp;#160; I’m also going to assume that when you write tests your test names have underscores separating all the words and making your test names human readable. Something like This_is_a_really_long_test_name_but_thats_ok_because_its_easy_to_read_when_I_use_underscores.&lt;/p&gt;  &lt;p&gt;The only (minor) problem I have with this approach is that underscore isn’t the easiest key to type.&amp;#160; I’d rather just hit the space bar and have Visual Studio change that space to an underscore for me – after all, that’s exactly what computers are good for :-)&lt;/p&gt;  &lt;p&gt;Here’s the good news, there’s a great little ReSharper macro that has been developed that does just that – it’s available from &lt;a href="https://github.com/joaroyen/ReSharperExtensions"&gt;https://github.com/joaroyen/ReSharperExtensions&lt;/a&gt; and all you need to do is create an R# live template that wires the macro up to your test name field in a live template.&amp;#160; For example:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/-sdA4nhHyejk/TrxG-Pn6wfI/AAAAAAAABOs/EvjjdlTbCLg/s1600-h/image3.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/-HwrA65auS7s/TrxG_61CKDI/AAAAAAAABO0/7JRf76ZytXs/image_thumb1.png?imgmax=800" width="675" height="241" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Now when you use the live template to create a test you can simply type the test name with spaces and they’ll be converted into underscores for you automagically.&amp;#160; Fanstastic!&lt;/p&gt;  &lt;p&gt;Kudos to &lt;a href="http://www.joaroyen.com/"&gt;Joar Oyen&lt;/a&gt; for his great work, and feel free to &lt;a href="http://www.joaroyen.com/2010/08/resharpers-live-templates-can-do.html"&gt;check out his blog post&lt;/a&gt; for how this works under the hood.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-3324292553191835452?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/3324292553191835452/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2011/11/underscores-in-test-names-are-pain-to.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/3324292553191835452'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/3324292553191835452'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2011/11/underscores-in-test-names-are-pain-to.html' title='Underscores in Test Names are a Pain to Type, Right? Not Anymore!'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/-HwrA65auS7s/TrxG_61CKDI/AAAAAAAABO0/7JRf76ZytXs/s72-c/image_thumb1.png?imgmax=800' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-7703821993716387899</id><published>2011-10-10T14:52:00.001+11:00</published><updated>2011-10-10T14:52:23.853+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='agile'/><category scheme='http://www.blogger.com/atom/ns#' term='scrum'/><title type='text'>Scrum is Open for Extension and Modification</title><content type='html'>&lt;p&gt;The title says it all really.&lt;/p&gt;  &lt;p&gt;Modifications to the framework need to be approved by Ken Schwaber and Jeff Sutherland (co-creators of Scrum), and to be honest there are likely to be very few, given it’s such a simple framework.&lt;/p&gt;  &lt;p&gt;The more interesting thing to keep an eye on will be the Extensions.&amp;#160; As a Scrum Coach and Trainer, I’m often asked what teams should do in a specific scenarios.&amp;#160; Scrum is silent on those situations because they aren’t generic enough, and yet there are generally accepted, healthy behaviours teams should have in those situations.&amp;#160; That’s the essence of an extension.&amp;#160; The healthy, beneficial practices team can use to complement Scrum to help teams in Situation X.&lt;/p&gt;  &lt;p&gt;This a is great move by &lt;a href="http://www.scrum.org"&gt;Scrum.org&lt;/a&gt; and a very welcome one!&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.scrum.org/scrum-extensions/"&gt;Scrum Extension Library&lt;/a&gt; and &lt;a href="http://www.scrum.org/extend-mod-scrum/"&gt;Submission Guide&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.scrum.org/scrum-guide-proposal/"&gt;Scrum Modification Submission Guide&lt;/a&gt;&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-7703821993716387899?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/7703821993716387899/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2011/10/scrum-is-open-for-extension-and.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/7703821993716387899'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/7703821993716387899'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2011/10/scrum-is-open-for-extension-and.html' title='Scrum is Open for Extension and Modification'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-3921000145695792540</id><published>2011-10-10T09:46:00.001+11:00</published><updated>2011-10-10T09:46:45.888+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='alt.net'/><category scheme='http://www.blogger.com/atom/ns#' term='community'/><title type='text'>Australian Alt.NET Open Spaces – Tickets Available</title><content type='html'>&lt;p&gt;The inaugural &lt;a href="http://ozaltnetopenspaces.squarespace.com/"&gt;Australian Alt.NET Opens Spaces unconference&lt;/a&gt; is happening on Sat Dec 3 in Melbourne, directly after the Melbourne YOW! event.&lt;/p&gt;  &lt;p&gt;(Free!) &lt;a href="http://ozaltnetopenspaces.eventbrite.com/"&gt;Tickets are now available&lt;/a&gt;.&amp;#160; &lt;/p&gt;  &lt;p&gt;And for the curious amongst you, the famous &lt;a href="http://altnetseattle.pbworks.com/w/page/12367896/AltNet-Seattle-2011"&gt;Seattle Alt.Net conference&lt;/a&gt; is run in exactly this style, and has the motto of “practice don’t preach” to indicate that it’s all about participation.&amp;#160; Have a search for people’s thoughts on the conference and you’ll invariably find people who found the conference much more enjoyable that traditional lecture style conferences.&lt;/p&gt;  &lt;p&gt;So what are you waiting for? Go and &lt;a href="http://ozaltnetopenspaces.eventbrite.com/"&gt;book a ticket&lt;/a&gt;! I’ve got mine, you should get your hands on one too.&lt;/p&gt;  &lt;p&gt;And don’t forget to spread the word!&amp;#160; The more we have there, the better! Rewteet this post, write your own tweets, put a post on Facebook or LinkedIn, blog about it, tell your colleagues; whatever works.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-3921000145695792540?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/3921000145695792540/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2011/10/australian-altnet-open-spaces-tickets.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/3921000145695792540'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/3921000145695792540'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2011/10/australian-altnet-open-spaces-tickets.html' title='Australian Alt.NET Open Spaces – Tickets Available'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-4346527285931481510</id><published>2011-09-19T08:58:00.001+10:00</published><updated>2011-09-19T08:58:13.150+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='TFS'/><title type='text'>TFS11 and Source Control Improvements</title><content type='html'>&lt;p&gt;Now that build is over and we have a Visual Studio 11 we can finally play with I’ve had a brief look at how local workspaces work.&lt;/p&gt;  &lt;p&gt;For those who aren’t aware, local workspaces finally removes the number 1, most annoying “feature” in TFS and that is the server-side source control model.&lt;/p&gt;  &lt;p&gt;This is the approach where the server keeps track of what it thinks you have on your local development machine and where all checkin/checkout operations require communication with the server making offline work very difficult, and where you can’t make local changes to checked out files because the server wouldn’t be aware of them so all files under source control have the read only bit set.&amp;#160; It’s a major pain.&lt;/p&gt;  &lt;p&gt;Now there are valid reasons for this approach related to managing VERY large source repositories (think multi-GB of source) and it’s still possible to use server-managed work spaces if desired, but for the 99% of us that just don’t deal with that sort of volume this is a feature we just don’t need.&amp;#160; TFS 11 sees the introduction of a subversion style approach to source control with the introduction of local workspaces.&amp;#160; Before you get too excited, remember that a local workspace is not a DVCS and TFS doesn’t feature one yet.&amp;#160; That said, Brian Harry in a recent post about source control improvements in TFS11 said the following (emphasis mine):&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;&lt;em&gt;I’m certain that about this time, I bunch of people are asking “but, did you implement DVCS”. The answer is no, not yet. You still can’t checkin while you are offline. And you can’t do history or branch merges, etc. Certain operations do still require you to be online. You won’t get big long hangs – but rather nice error messages that tell you you need to be online. &lt;strong&gt;DVCS is definitely in our future&lt;/strong&gt; and this is a step in that direction but there’s another step yet to take.&lt;/em&gt;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;There’s also a number of suggestions about source control on the &lt;a href="http://visualstudio.uservoice.com/"&gt;Visual Studio User Voice site&lt;/a&gt; that you may want to vote on so that the team makes this a priority, for example &lt;a href="http://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/2037673-allow-the-version-control-system-to-be-pluggable"&gt;Allow the version control system to be pluggable&lt;/a&gt;, or you can create one specifically for a TFS DVCS feature.&lt;/p&gt;  &lt;p&gt;Anyway, let’s have a look at a few things in the new developer preview.&lt;/p&gt;  &lt;h3&gt;What’s on our disk after doing a Get Latest?&lt;/h3&gt;  &lt;p&gt;Firstly, we see we now have a hidden folder in our workspace root&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/-Rb3Nz69KV-g/TnZ3XR8kiJI/AAAAAAAABNk/o5T4toGriDI/s1600-h/clip_image002%25255B4%25255D.jpg"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="clip_image002" border="0" alt="clip_image002" src="http://lh4.ggpht.com/-IKqpcE_Hlas/TnZ3YKs1N4I/AAAAAAAABNo/Egjc3Ebdo7o/clip_image002_thumb%25255B1%25255D.jpg?imgmax=800" width="523" height="226" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;The folder itself contains a bunch of child folders, which are oddly reminiscent of sourcesafe.&amp;#160; Thankfully it’s not!   &lt;br /&gt;&lt;a href="http://lh3.ggpht.com/-gFk1zWYYMIo/TnZ3YzxJbSI/AAAAAAAABNs/GV3qnAmc4Vg/s1600-h/clip_image004%25255B4%25255D.jpg"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="clip_image004" border="0" alt="clip_image004" src="http://lh4.ggpht.com/-x5w3mLLA8IE/TnZ3Z46mGwI/AAAAAAAABNw/VSrJw_W4Gs0/clip_image004_thumb%25255B1%25255D.jpg?imgmax=800" width="634" height="445" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;These folders course contain GUID based file names:   &lt;br /&gt;&lt;a href="http://lh6.ggpht.com/-lv2uH7lAP_8/TnZ3aqalmLI/AAAAAAAABN0/jsfP5V0PDZ8/s1600-h/clip_image006%25255B5%25255D.jpg"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="clip_image006" border="0" alt="clip_image006" src="http://lh3.ggpht.com/-9-SdSWQ_CQ4/TnZ3bhxGvRI/AAAAAAAABN4/9sjiXt33uNU/clip_image006_thumb%25255B2%25255D.jpg?imgmax=800" width="634" height="445" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;And each of those files are simply GZipped copies of your individual source files. Opening them with 7Zip reveals the source files&amp;#160; just as you would expect.&amp;#160; Obviously you should leave all this alone and not touch it but it’s interesting nonetheless (or at least it is for me!)&lt;/p&gt;  &lt;h3&gt;Local Source Control Operations&lt;/h3&gt;  &lt;p&gt;So leaving that aside, what happens when we make file system changes outside of Visual Studio?&lt;/p&gt;  &lt;p&gt;To find out I did a simple copy/paste of a few files and renamed a view in an MVC3 project.&amp;#160; This is what pending changes shows (the other changes are from the VS11 project upgrade)&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/-WHYVyF1yfUw/TnZ3cU7J-XI/AAAAAAAABN8/howIpfGJUqE/s1600-h/clip_image008%25255B4%25255D.jpg"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="clip_image008" border="0" alt="clip_image008" src="http://lh5.ggpht.com/-2BFGdMnJOBw/TnZ3dEcW8lI/AAAAAAAABOA/4dQ1Sea65UY/clip_image008_thumb%25255B1%25255D.jpg?imgmax=800" width="289" height="491" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;So the changes are detected but are currently marked as ignored for now.&amp;#160; I get why the copied files are excluded, because they aren’t part of the solution, but I wasn’t sure why the rename was ignored until I realised it had happened because the files I’d chosen were from an MVC3 project and I hadn’t updated VS11 to support MVC3 apps as yet.&amp;#160; This mean Visual Studio hadn’t actually loaded the project so wasn’t actively tracking the file that was renamed.&lt;/p&gt;  &lt;p&gt;If I click the “Detected changes (5)” link, we see this:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/-xD11Mgko0MM/TnZ3dkZjsII/AAAAAAAABOE/ZPGDPF0xoyI/s1600-h/clip_image010%25255B4%25255D.gif"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="clip_image010" border="0" alt="clip_image010" src="http://lh5.ggpht.com/-UkqWHbbcDwQ/TnZ3ebYJ5jI/AAAAAAAABOI/mrS_JwLbJ8I/clip_image010_thumb%25255B1%25255D.gif?imgmax=800" width="549" height="361" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;You’ll note that the rename detection isn’t happening in the developer preview though I expect a way to mark add/delete pairs as renames will be provided before RTM.&amp;#160; We also have a “promote” button to turn the excluded changes into included ones.&amp;#160; Pretty simple.&amp;#160; You can also right click files to ignore them (such as upgrade reports, backup folders, etc) so that they don’t constantly sit in the “excluded files” list. &lt;/p&gt;  &lt;p&gt;Once we include the files we want, we see this:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/-39mLnRGVSzg/TnZ3e6pluII/AAAAAAAABOM/YVFA70lGb4o/s1600-h/clip_image012%25255B4%25255D.jpg"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="clip_image012" border="0" alt="clip_image012" src="http://lh4.ggpht.com/--f9Z-1qzk0I/TnZ3fiNvzVI/AAAAAAAABOQ/ADfmYV4vN90/clip_image012_thumb%25255B1%25255D.jpg?imgmax=800" width="272" height="324" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Nice.&amp;#160; Now, when we’re offline we can’t check in with a local workspace.&amp;#160; We have to be connected.&amp;#160; When we are, we can click the Check In button and we’ll get a notification message with a link to the Changeset we just added&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/-DFsqGRmEtr0/TnZ3gEU-7DI/AAAAAAAABOU/cP9neTxuhGM/s1600-h/clip_image014%25255B4%25255D.jpg"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="clip_image014" border="0" alt="clip_image014" src="http://lh3.ggpht.com/-h4Rz2oZn8K0/TnZ3gzF5-tI/AAAAAAAABOY/SLMn2esyxxc/clip_image014_thumb%25255B1%25255D.jpg?imgmax=800" width="304" height="227" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;h3&gt;Local Exclusions&lt;/h3&gt;  &lt;p&gt;In git, mercurial and other source control systems there are usually configuration files to control the files that source control will ignore (i.e .gitignore and .hgignore) and they are located in the same folder as the source itself so that they can be checked in and shared across all team members.&lt;/p&gt;  &lt;p&gt;In TFS there is only one global exclusion file, and it contains all your local exclusions.&amp;#160; It lives in C:\Users\Me\AppData\Local\Microsoft\Team Foundation\4.0\Configuration\VersionControl\LocalItemExclusions.config and looks something like this:&lt;/p&gt;  &lt;p&gt;&amp;#160;&amp;#160;&amp;#160; &amp;lt;UserExclusions&amp;gt;   &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &amp;lt;Exclusion&amp;gt;c:\temp\tfspreview\scrum project\Trunk\DemoAppSolution\UpgradeLog.XML&amp;lt;/Exclusion&amp;gt;    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &amp;lt;Exclusion&amp;gt;c:\temp\tfspreview\scrum project\Trunk\DemoAppSolution\_UpgradeReport_Files&amp;lt;/Exclusion&amp;gt;    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &amp;lt;Exclusion&amp;gt;c:\temp\tfspreview\scrum project\Trunk\DemoAppSolution\Backup&amp;lt;/Exclusion&amp;gt;    &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &amp;lt;/UserExclusions&amp;gt;&lt;/p&gt;  &lt;p&gt;I’d prefer a local exclusion file (e.g. a .$tfsignore) to live at the solution or workspace root and I’d also prefer it to be glob or regex syntax instead of and XML document, but at least we have something. It’s a start.&lt;/p&gt;  &lt;p&gt;Finally, if you want to play with local workspaces you’ll need access to a TFS11 server.&amp;#160; The hosted TFS preview service works well for this and if you try doing source control operations over a 3g connection you &lt;i&gt;really&lt;/i&gt; feel the difference between local operations and server calls.&amp;#160; Alternatively you can download the TFS11 developer preview from MSDN subscriber downloads and install your own TFS server to have a play with.&lt;/p&gt;  &lt;p&gt;All up, this is a well overdue improvement in source control and should alleviate some of the pain we all feel when dealing with TFS.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-4346527285931481510?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/4346527285931481510/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2011/09/tfs11-and-source-control-improvements.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/4346527285931481510'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/4346527285931481510'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2011/09/tfs11-and-source-control-improvements.html' title='TFS11 and Source Control Improvements'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/-IKqpcE_Hlas/TnZ3YKs1N4I/AAAAAAAABNo/Egjc3Ebdo7o/s72-c/clip_image002_thumb%25255B1%25255D.jpg?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-4338694964048167336</id><published>2011-09-07T14:32:00.001+10:00</published><updated>2011-09-07T14:32:28.883+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='presentations'/><category scheme='http://www.blogger.com/atom/ns#' term='tech.ed'/><title type='text'>Learning from a FAIL</title><content type='html'>&lt;p&gt;&lt;img style="margin: 0px 10px 0px 0px; display: inline; float: left" align="left" src="data:image/jpg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAkGBhQPEBAPDxQQDw8PEBAPEA8QDxAPEBANFBAVFBUQFBQXHSYeGBkjGRQSHzAhIycqLCwsFR4xNjAqNSYrLSkBCQoKDQwOFA8PFCkcFxwpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSwpKSwpKSkpKSkpKSksKSksKf/AABEIARMAtwMBIgACEQEDEQH/xAAcAAABBQEBAQAAAAAAAAAAAAAAAQIDBAUGBwj/xABGEAACAQMCAwUFBAYIBAcBAAABAgMABBESIQUTMQYiQVFhFDJxgbFCUmKRI3KCkqHRBxUkMzTB4fBDU2OUVIOywsPT8UT/xAAZAQEBAQEBAQAAAAAAAAAAAAAAAQIDBAX/xAAgEQEBAQEAAQQDAQAAAAAAAAAAARECEgMhMVEyQWET/9oADAMBAAIRAxEAPwDYxT0FNFSLVQ4UtLRQFAooxUUYoxS0YoGEVFkHOCDg4PofI1YIrkL++eG6lZDjvbjqCMDYis1XSslQtHUXDeMJPt7snih8fVT41dZKxjUqi0NRNb1oaKbyqKpwQb1u2rbVRSKr1uK6Riuq7Ne7J+sv0NbVYvZf3ZP1l+hrb01uOdNoxTtNBFENxRS4ooG4op1FB5eBUgpgqRajZ1LSCloAClpaKBKUUhpy1FGK4ntLZSRTPLIhWKRgUmQal6AaZAPHrv1+NdxitCOIPGFYBlZcFSAQR5EVMS3HknMwV3AJ3Uhtm9Vbx+tdDwvtFjCT7jwk8f2v51Dxvs+kHErWKHKxXAkkaM95FkB0hgD8aq8T4RJbtpmURk7LIMmCQ+jH3D+FvzrOVqV2K4YAggg7gjcEUhSuPsOKPbtgZK570bZx8vI11fD+IpOModx7yH3h/p60XVhUqxGKYBUqCtRl0/ZX3ZP1l+hrcrD7K+7L+sv0NbtVmkpKWiqhKTFOooG0UtFB5ctPFMWnijR1OFNFLQPzQDTaM0DiaFNRM+K5/tFcRDS80c06oGysbFVGcbsNa56etFdHLcqm7sqD8bKv1p0fau0VVBuYCce6kglb91MmuLkijOuKHh8KOEWXVJyidOvbOlWJB0MDv0zvW3wbit4sdqiJaxC6WWdTiQhQf0pwupQB3wOv51Gapcd47HJxKweJbiUIkgwttMrNlx7ocLn4108/EZZlZPYJnRhgi4ltIlI9RrY/wrhuM31zJfWTPNGrmaa3Vo4kUKq3nJLYYtn3Sd63L7mAX2u8uWNvAHjCSlMyGKRsfoQud1UfP1olchx3nQXDwCKKNMoEjMzzmIsF2VwoJG/Sm8HuX5il7qCDToJZVTulgcqS7YyMb7eNQ9ouGr7cq/pJQRFqMhdyzERAjLk+bb1LYcIBCqsa5a9CjLKO6Lkd0Y391SP9mpjcdRFfqzYh4orSj7Mq2jxN+yqofyatNL+7i3lgjuE/5lpLhsefKlx/BjXC8bsj7ddao1ZOTCVUSMArRGJ5AhPvMEVtj4aj4Yq+bdrd5TA9xaj2qJcDLwiOSJSMsmUO7ZxpO2PWqPU+yHa62JeJ5BbzMy6YblWtpGODsokxq/ZJrtc147wHtJc8toriC14lA7XGpDp1Yt3Kk6QGB23yUA9fKf8Ar+3iMQsZbrhEjNHqgl/TWQEq6kJRy0aKQDupQ9PXFxnXrdFcrF2sltgDxCNTAenEbItPaEfelTd4fj3l/FXTWt2kyLJE6SRuMq6MHRh5hhsaKkxRiloqBtFLRRHllOFNpRVVJmimg0uaB1NJozQTQQyGsbjUBeMrnSHeOMnA6NIq7+m9bMgrNuO/IkPhkTSH8KOulfm4B+CHzopJ7fv3chLuyWqd0uVD4WZ/DoM528xmtrh/CIlksk0hlSzkwTkliGt0yQT5H61iwsZVTO3tzEt+GzRCQnxKkAn/AKreQrZikZonlBKvLMtjEw/4UIuBCzL+InmPn9TyqJXN3VqvtvCe6p1tdM+FGGbnyMW+GrcehrqL4Zh4ljq8nJQjYs5ghRYRj/qMy49TXN9pbzk8RiEYCpZ2b8pfAERmQD4bRj5GurS0EctlD1WOK4mydy86iJeY3mx50rZ82zRHBdsnC8TlcnCoYCx8ABpP0B/hRwGPDW8eMP7Wz6fFUXJOf3lX4tVTj7lryUncm/CnP3VuljA/dVRWnwVc3s3nrAX5yvn6LUrUWJY2M4ukBKuLiRAu78pUEBlQeLYcSAeIyOpFMEqmJlQaHFjDDEEJ/wAVDqdGUj3iV5bxn7QDAfaFa3CjleG4/wDDMPhiGIH+OKrWFsknsikZSdb5cDbNpzjJEw8tJMRU+GvbrVGnwXhcLvK8YQqLy1EbKArNFeR4kkEi4OO8WXqNSEdDgY3aTgkvP4U2pZVR5ISJcMrf1eJhK4CAOYygZgOvUb93OpwCz1xGQYLyWT3WN1DywOhkU4xhZeYrbe66h1wxJLe01uUubJOZrWWeVYuaMEx8Rsp1zqGysXWQHII1BW2ywrURlWkElkddvLNZsoiDlNVxaN+mkjZpUwWUEGE95STqB+0KktO12jEkSpYX0hj0yQEPwq+kYIzRzxglY5MPgsCGB8fCt+/v2ZZJJIW5rNJKNC5dbg2vsq6QO93ZoUwB0bA95U18Z2z4fE6CS0kWRWmgTklj+liMjR26tggt3IBkt3gZcZyCATHtXZrtCLyN9SGG4gcw3Vuxy0M4AOM/aQghlYbEEVrmvGf6IOJSvxKWF9Y5dk0UiyHMmmKVOSHb7RQPIoP3WXyr2asqSiiiivLKXNNpaqHCikoJoHUhNJmkLUCNWXnTdKT0liMYP40fWF+JVnP7BrSL1n3VpHKRzV5gU6grE6dXnjxPxqKgt7gRR2rOQvsrG2nyQNAKcvWc9BkRNn7rZq+eNQxxTQ86DmxTLeQAzR4kUzCfQDnGrWJUx4ZU9DVeLh1upytvb58zFGT+ZGa1bfixjULGkSADHdTH0oOb41xO3l4nbSozyRT2zJIEgmZhuRjSFzkq2PTTW9HxOUpasLe8ee1blt/Z2iWaEryncNIVxkBJADjdcHzqO8lM00c7/wB5CGWMjIADEE7ePSp24nKftt8sD6VDHC9pzI15cBYZI+bLG663hV0kYJk91mHvAN18au8IS5M/MCW6us8qyBp3KtqKEgaUOAGUEHPiRXQyRBmLsAznBLEAsSOm/wAhTgtBnzcGux0ktdH6cGJGljZYpnWR0SYg6d1xnTkAkDGxFO544YCxmCQsY+QJIJYJktbYfYgjVtbOcDvMFGQu2FweigXrUqRAbAADIOAABkHIP5gVRk8N7YWiI6NJ7MbiBLOANHMfZeHnaRyQp1TMNIVV1bqmTjUau9qOJJPeWR0TxaL+BljaLlFeH2tvcMCxkKhC5aUgMR3QD1DAXCX0NGHcRsuNGmJ1zv3u+hJ6nxrA412QjlEYtg8AF3BO6rcSKoQK6yui+6shyhBCj3eu9UdNc8Qd1lk9mkVWivrphJJFGRb3OlYVIDMQ7lAwUgEspx0rie39wcSFkt4pVv4hI8dwGkSbRG8koGgacsGJPiZRsdIxt3fYtiZZIZorgLLbsq3hJncLysuZMEO2dSkyI4AGwrF4xF7KJklto7eRb6z2ZGmhEYe2cxiVdsuV1ucszD7KUEnY7jrWXHoo55YLjnQ+wNNAVC7SaY2b8WqNR191l9BXvWa+Uu1HDieIXGklxIRNrOkklgCxIVsAlj7oJxkDO1e4/wBEHal76yaOdi9xZuIXc5JkiIzG5J3JwGU537m+5NSkd3RRRUV5ZRRRWkGaKKQmiDNMLUUxjRTJHqDNPc1EDUVKtOBqIOPMfnRz1H2h+YqCfNFVzeJ94fLJpDxBPM/kaC1mjNUzxFfxfkP50n9ZjyP8KDTt/GrArFi4wAfdOPiKspxtD11L8s/Q1UaQFOxVSPiUZ6Ovz7v1q0jg7jB9Qc0Eiio7uATRSQPvFM8byJth2jdGXV5/3aD4DFPBpy0HA9tOzHKm9rtQUgMYWSNmLrE5YDSud+X7uAc43HlXWf0C2rleIXJyI5JIYUHnJErlz8uYo/8AytKWwFwjwNsJlaInGcaxpzjxxnPyrteA8FjsbeK1gGmOFQo82PVnbzZiST6mpRpUlNzS1lXltGaKStoU0006kIoIzTGNSGomoInrL4lHkA+VaclVLlMipVYuSKcJiOtSvHUZjrLRwnqQSVXMdJpNTTFsPS5qqJCKes1XTE9LTFfNPqoKFYjcEqfMEj6UtJRVmLisq9GJ9Gw313q7D2jI99AfVD/kf51kUmaamO44NxBJXTQckOuQdmHeHhXoGa8c7MWMkt1DysjS6szeCxqwLEn4ZHzFew5qUh2aKbmkqDzLNFMzT1royUUGlpKoiao2qZhUJqCJhUEi7VZIqN1qKzJY6hMdX5I6gaOstKpjpOXVnl0vLqCoYqTkVdEdOSGioLXh+ogZIGa6WHgkWBsT8WP+VULOKtuE7VqMVo8B7O27l9cStjTjUXPXPrW2nZy2HSCH5oD9ap9nG/vPgv1NbeqpViunCoV6Qwj4Qx/yqYWyDoiD4Io/yp2qjVUU4bUZpmqjNA/NFR6qKDznTTloxS11YFJS0VA0ioytS0hFBXZaaVqVhSYqKpyJUTJVx0qIpUVW5dHLqcpShKmKhCVIsdSCOpUjpIHwLWlFVONauR1qMt7s828nwX6mtnVWFwE7v8F+prYzUqxLqo1VFmlzUVJro11Fqo1UEmqiotVFMHCClpBS10cxRRRUUlFLRQRsKYRUpFNxQRlaaVqXFIVqKh00aKlK0aaBgSpUSgLTxQSRip0qJamSqjW4Id3+A+prW1Vj8GPef4D61q5qNQ/VRqpmaNVRT9VGqoy1Gqgk1UVHqooOLpaSlzW3MtFJRRS0lLRUDTSU6gCgbSGnYpDUUhoApaBQIBTwaQCnKKCVRUqVGKetVGlwlu83wH1rT1Vi2QfLcoAkDfUcbZq7aQzs4EmhE33U6m6dMHasXqR055tXdVGqpW4WT/xCPgi1FDwgq2rnMSdsMiEEdcY/OsX1P43PT/o1U9Iien1HSny8LyAFYoQScqux9CDtU1rZ8vBLFyFK5IAyC2rwpfUX/NUkikB2Qt66kH1NFTcXtXmj0pK9ucgl4wpYj7u/T/Sis+fS+HLiKWmilzXpeUtFJSigKWikooNJQTTdVQOoNIppSKBppwFGKWoAUq0lKKolFPWq802kFjnYZwBknA6AVCOKAAs0c4ABJPKJwAMk7Gg6LgvvSfqf+4VrW57w+dctwfjCsXwtx7pXa2uMghhkEBCRWpa8XUONQnHUb2l11xn7lcu/mu3Hw2n4vGpKnVkbY0k7/Kp42Db+eCM14l2+sp557wwQXLI7xsjCCZdZyucZUbACvUOD9oIEhhieVElS3hDxvqV1IjUHIIz1rlldrY6GkdsDPlVS24rFKdMcsbt91XUt+XWrLdKCsvEY5MqjBj12wcDPpRXOdlOxrWDSu0qy61K4CFcZnaTxJ8Gx8qKRKyBSim04GvW8ZaBSUA0Ds0maTNITQDNWdeXEmpVi5Y65Mms5PgBpI9atTSVy/aC25pjJUviWFR7pADTx5GD4kAj86K257i5RdWLU95F6zKBqYLknBwN6dbXNy6K4S272f+LN4EjY6DkbdfEYrmbzhCYviIcBIFKd2LutypDnb9jp9Ku/1MguIk5ThORIWUIvvCSEA4HoX9d/hUG/zLn/AJdv/wBxKP8A4qOfcf8AJh/7p/8A6a5a3sV5VqSJgzzlXIDjMeJtsg7+6n5Ut1baY71la4DRE8rvTjH9mjffDbd5moOkn4hPGrO0EWEVmOLok4AycDlU2Hic7Fh7OAUbSwN0mQdIYfZx0YVkSW39oEfMuRGYWYgyXPvc1V6a+mkt6eNZXC2KwQkTTIXuzE4Esw7nNkXPXHRV9dqDqL6/nKleRpJ2yLiM9fgKy7RLhuUumUh3njYG+cBtJbCnrjAB+OK5+74lKJb1DcyaYEVocs+WYxltiPUeNLLLIvOxPO3JeFl0vOAecZNUg32wGUEnrvjrvR2HZiO7DRKhdeZBI2Wu37zh4ssSUO/eO2/8Bm5Nxa+iRXM2NVtFM2rDBWflqHY8vYd4+Y2rzzi1nKqM4a7wl3JHltaroaIOpUM2VY5LY/FWFMkmmIsJtDdwsy4Q4kAKqfyzXPqbXTm5HtT8eulGTfRp/ZBcDXaxDMm/6LfG/Tf+FV4e1LLLI7cRhOLMXCsIYlHtJV1MeNwGCxJn08q8tksZFDgK6YM8RUzYIYCNiuF9FJx449KhsLVnExIjbl2s7kOZZSABKMqPvAuCD0GNR2DVJyt6el8N7cPPf8MhkuFu0l5TuDGInhumJ7oCoNW3ntv1r1rNfLT35tJLO4haMzwFH08lUVJUVDpbxbJzkk+fSvpm3u2ZE1IMsiFtLBkyVBOk+I32NY7mN+nd1ZkOxorOvr+2gYLM8MTMMqHKrkZ6jNFYbchS03NKDXreMtGaSkJoFJqNmpSajY0EMxrMvB34B9r2gMB+FY3LH5D+OK0pDWM8+l7mbqbeLSg8NRTmN+f6Mfs1FTz7xXhG+u4jVfxuBAmj1GsFcfGr7uBczMThUto9TZ9zvytt5d0An4LVa2tcSW8HUQxtMxP2pshAx9ctK3xwaiiHMhjz/wD23Gt/WHvSBPhy4kT5mglto8Dh6HYrEzMvkRbAFj6gvj9s02XeC48dd5j9dfaI49HwwNHyqSa5Km8uOrQryYwendjEh/N3AP6gp622l7e2G6wo07E9XdSEUn4u7v8AFRQTj/FyHO/s8OD93Ms2fzwD+zXPdnDm2tAehvJiR4ltE7A/vYNackhMU8qnD3EvIjP3U5ns6EfPW/7RqrwVBzpEG0drLcEDyaSRlX8kR/36ClboPb7kN7ksUvOB6OiOUUDyIBAz6H5QRcSDxuWkUmWwSNlLhebIHVgPQqJJMsPHNTXGQIZUwJpInuF/XaXmaD6FZ2X5A+ApIeKuOWYjsQ9xbggd1yMywH8DA5x4EE+AoHX3FNpm5+ctw1ldHXU0iACW5GOhHLIOPu/CoeI3+soNYwOKxyYfSQoYbsoPgzByfAnVS3FyHUBVQgKbq37oyFODJb/qHUMDw1fhFZfEhCghyiMiTW7ocDJtpNWUbzwVPXwx61jqbXTmyTFm2iChdas/6SIYA1MDAH5y7eIJ1geI2Gax5IeUkxU4JjZAVyQUMUqsVY7d7Sx9Q7jqMVuizj1CPSAQ7wB1ADBuXzIpQR9sDKk+PjWXdwpynfB3gjKrqbC8xJdSgZ6awrgehrMlatihcWw/QkBQWlK+6BuExnAJHkfiR4EZ6/8Ao47eTW7GwcrJHhmg1ZPL07mMb7LjcDw+dcjxyQRww8osnhjWxAjNuhI36YaWUftGrvYrhTrKLqUFU0lU1AgsW7uoDywSKvX43U4/KY77tHeC/KyXCqixrpQo3XLd7VnO+QBRWasynunOHUE9feVvD16flRXk2/b1Y6TNKDTKM19B887NITSZpDQKajY04mo2NAxhWHdQOzzLGnMS4i0v3xGUcKUzv1yCP3fWttmxVBmyTUU2K4nEscrW7kiIxyhJoDltSsGXLDbIfrjZqgillSOBDb3Gq2lzsIWDQ4kTwc97lvnHmtWwT5t+81O1t95vzz9amilcXRKXicq5HOzJETbyEazEo0nSDjvID+16VYPHIxOkpEqK8LI2u3nXQ4kVlU5Xx1ONvECpua33j+S/ypj6z9th6DA+lNVnf15CsCKZFDQ3IbBypaMXJOsAjcaGz8jVCy45GJL9VdW57syNrUA++vUn1U/Otk2x8wfiD/Oq0PC9OvIjfXI0m46ZA2GR6Vnb9Lk+3PXFwHEBeVVUQFt3GwAjGAPP09KoQTBRFiTUVV5NKZc6iDiMafHc/DFdtFwlc6hHCp8wgzn5CrKcNPmB8F/1qe/0vs4SNpQECCZikJVSI5ACzYzgsBsNIqG+tpSI1ZHC64Ik1FBkLqwNicZJNejDhg8Sx/IU7+qYzjUurByNRJww8R61ZKlscL7Pdklu4pLO+d2Idk0AjYdF2FSjszcSqRqI7uAqqFXaMoq532wSOv2j516BHbqOiqPkKsKKuX7TY5XgnYhEEZkRQ4C6mZuY2vG+M7DfPSqnGOIsOYgONMhUbgnuvsf4V3UdeR8ZJ9oueu08mPL3zXP1OfZvjuzW1Febocb6cdRv1yaK5xGJHvHJOdgMrS15/F3nqa9bBpc0zVS5r3vGXNIWpuaTNAE1GzUrNUEj0DJXquDT3aqvtKg4JxUqrYNLUKzqfEfmKkDiop4pc03NGaIdSUmaKCxD0qYVDB0+dS5qh9ApgNMlu0Td2RP1mC/WqLQqRTWHP2st0+3rPkis2/lnpWNe9vm6QxgbbM51H90fzqeUn7MdpcXaxK0kjBEUZJJx8vj6V5RfXfNmlkxs8juB1IDMTg02+4tLcNqmcvj3QchQfRRsKpAYOd9/IZFcurqz2T5APu0U1VxvvjwJorm3ses5pc0zNGqvU5HE00mkJppNAjGoJDUpqCSoqGVqzLld60XqncJUqxV00oWnaacFqKVJGHQn8zU6Tt5molWpFWgnSc/7FTI5NQKtWYhViLMHTFXEiHjvVSOrkZqoze1Xds5SuxGjBBIP94vjXm7zkjLdfjkj1Nek9rf8FNgZOE2/8xa81e2ZcgqVxjJx59Dnw2zWO41PhGMn7S+gxjand49Ovjv+WP5ULb5ydyQCxI8ABuxPlVaVyrFWyCp3yd8g9M5rHjpiwCfAL476tyevnQ4Pnv4nfpVYXAJBPeHh8T1qRnG+O7t1BJHwqeK4nCfDI+PSiqwbbOflnG386Kvivi9dFFFFd3IGmUtFA1ulQS0UVlpXNVp6KKVEFOFFFZaPFSpRRVEq1YSiiqysR1aiooqitx7/AA0vwH/rFec8MiEt/BHINSSFQ6noQdXlRRUrXPw6finZq3js7iVIwJEUFW1OcfpAOhOOhNefsg73wJ/iKWiscrVWfYgDptVqH+VFFaqz5RXihSSNv9iiiitRnr5f/9k=" /&gt;Last year at TechEd Australia I delivered a session on Unit Testing that was rated top 10 overall.&amp;#160; This year I delivered a group session that has ended up in the bottom 20.&amp;#160; Ouch!&lt;/p&gt;  &lt;p&gt;So what went wrong?&amp;#160; Let me run through a few things and explain.&lt;/p&gt;  &lt;p&gt;For those who weren’t there, the session was a group session presented by myself and 3 others and what we wanted to show was how you can use the full Microsoft stack do deliver on the “three screens and a cloud” promise, how the ALM tooling that Microsoft provides (i.e. Visual Studio and TFS) helps with that, and some of the things to watch out for when doing this kind of development.&amp;#160; Our vehicle for this was going to be a multiplayer game that runs on both Windows Phone 7 devices and Windows desktops.&amp;#160; We also wanted to show how you could extend the experience beyond the game itself and use a single page web application to extend the gaming experience, so we threw that into the mix as well.&amp;#160; That’s a lot for an audience to process in a short space of time.&amp;#160; Probably too much as it turns out.&lt;/p&gt;  &lt;p&gt;Adding to this, re-reading our session abstract I can see how it is easily misinterpreted.&amp;#160; It can be read as if we were going to actually build the application on stage.&amp;#160; We never even considered that as an option because we though for sure that the audience would be lost, but it appears a number of people thought we would try anyway.&amp;#160; I appreciate that you think we can condense 170 hours of development effort into a a 1 cohesive presentation, but we’re not superhuman! :-) This is a failure to manage expectations on my part.&amp;#160; If you turn up expecting to see pure, unadulterated, glowing awesomeness on stage and then we present something that makes them seem merely human then you’re going to be disappointed no matter what.&amp;#160; Not meeting expectations is a common cause of discontent and I missed the mark here.&lt;/p&gt;  &lt;p&gt;Now when we started our session planning we had nothing; no application and only the vaguest of idea on what it might be that we’re going to build, yet we needed something to show so we could have the bones of our session.&amp;#160; This meant we needed to build the whole thing in our spare time and this is where the main problem arose – for various reasons the team only finished the application right before TechEd.&amp;#160; Further, because of the last minute finish of the application, the remoteness of each of the presenters, and the presenter’s individual TechEd sessions on top of this one, we never managed to get a proper dry run through the presentation with all of us there.&amp;#160; We knew the basic idea of what we needed to talk about but we never managed a full practice first.&lt;/p&gt;  &lt;p&gt;This in particular was bad! When I’m doing a solo presentation I usually do at least 3 or 4 dry runs first just to make sure the timing is right, that the flow works and that I hit all the points as I want, etc.&amp;#160; Preparation is key!&amp;#160; I know this, yet did we do it for this particular session? Not really, no.&amp;#160; And the results, sadly, showed this.&lt;/p&gt;  &lt;p&gt;Because of the lack of preparation, we also had a few “off script” moments that really, really didn’t help.&amp;#160; Compounding this is that as presenters we all know each other and are quite ready to joke around and have fun while still getting stuff done.&amp;#160; This unfortunately doesn’t translate on stage.&amp;#160; Humour in a session is great on the proviso it doesn’t detract from the message, that it keeps audience interest up and reinforces the learning for them.&amp;#160; You’ve also got to ensure it’s delivered well and in-jokes or overdoing it certainly doesn’t count in any of these cases.&amp;#160; Again, we let the audience down a little by doing this.&lt;/p&gt;  &lt;h3&gt;Lessons Learned&lt;/h3&gt;  &lt;p&gt;So, what do I learn? How do I improve for next time?&lt;/p&gt;  &lt;p&gt;Content may be king but don’t overload the audience or be too shallow and too broad. We would have been better scaling back on what we did, limiting our content to only a few areas and going deeper in those areas instead of skimming.&lt;/p&gt;  &lt;p&gt;Preparation is paramount.&amp;#160; Our lack of preparation hurt.&amp;#160; Not the preparation in the building of the game (that was fine) but lack of preparation in the delivery of the session.&amp;#160; No matter how good (or bad) the software was, if the delivery was good we would have been fine and people would have enjoyed it more.&amp;#160; The lack of preparation also contributed to overload of humour on stage.&lt;/p&gt;  &lt;p&gt;Set realistic expectations.&amp;#160; There’s a trick when writing session abstracts.&amp;#160; If you make your session sound too boring you probably won’t get asked to speak, but if you over-hype it then people get their expectations raised too far and you set yourself up to disappoint.&amp;#160; More care and attention to the wording of the session abstract would have helped a great deal in this case.&amp;#160; It’s much better to under-commit and over-deliver than to do the opposite.&lt;/p&gt;  &lt;p&gt;Working in a group is hard.&amp;#160; The larger the group, the harder it gets.&amp;#160; If I do another group session in the future, the way we work and the amount of time we’ll commit to up front will be clear from the outset.&amp;#160; Coordinating 4 speakers when they each had their own individual sessions and limited availability was asking too much from everyone, so when something had to give it was, unsurprisingly, the group session that suffered.&lt;/p&gt;  &lt;h3&gt;Evaluation Comments&lt;/h3&gt;  &lt;p&gt;To round out this lengthy post, let’s have a look at some of the comments from the evaluations, both the positive and the negative.&amp;#160; I’ll provide my own feedback in italics:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Although I gained a number of tips regarding potential issues there was little here that I can action directly when I get back to work on Monday. The speakers were all fun and had an easy rapport which made this an enjoyable session&lt;/li&gt;    &lt;li&gt;Cut the attempts at comedy and get on with presenting what would have been worth seeing at a more even pace. If you hadn't have jerked around with your piss weak in jokes at the start you could have maybe finished up properly. This doesn't apply to Steve. He looked embarrassed to be with you other gooses.     &lt;br /&gt;&lt;em&gt;- Whoa! OK, ignoring the bile, your point is taken.&lt;/em&gt;&lt;/li&gt;    &lt;li&gt;Funny presentation, sometimes a bit over the top, but great stuff nonetheless     &lt;br /&gt;&lt;em&gt;- Point taken on the over the top.&amp;#160; It shouldn’t have happened. Sorry.&lt;/em&gt;&lt;/li&gt;    &lt;li&gt;good fun talk, relaxed&lt;/li&gt;    &lt;li&gt;Good stuff Ricahrd!!&lt;/li&gt;    &lt;li&gt;great presentation - thanks!     &lt;br /&gt;&lt;em&gt;- Not our best effort but also not a complete waste of time.&amp;#160; Good to know.&lt;/em&gt;&lt;/li&gt;    &lt;li&gt;I think the material was good but the comedy act they tried to tack on was just distracting and made their presentation look amateurish which is not what I expect for this type of event.     &lt;br /&gt;&lt;em&gt;- Agreed.&amp;#160; Comedy should have been used sparingly.&amp;#160; Mea culpa.&lt;/em&gt;&lt;/li&gt;    &lt;li&gt;I was looking forward to more on the fly coding, rather than flipping past a pre built project. It would be better for my understanding if the demo was for a simpler project, but setup and built in front of us.     &lt;br /&gt;&lt;em&gt;- Expectation management.&amp;#160; We probably should have been clearer in the session description about this.&amp;#160; Lesson learnt.&lt;/em&gt;&lt;/li&gt;    &lt;li&gt;Its teched not the comedy festival ;)     &lt;br /&gt;&lt;em&gt;- I assume the smiley meant they enjoyed it.&amp;#160; Still, backs up the point that we overdid things.&lt;/em&gt;&lt;/li&gt;    &lt;li&gt;Less talks and more information and code demo was desired&lt;/li&gt;    &lt;li&gt;Loved the idea of the talk but the reality wasn't what I was expecting. I was expecting (yes my expectations were probably too high) a simple MMO built from scratch during the presentation, demonstrating the key technologies for each platform. In reality the app had already been built but even then too much time was spent monkeying around instead of showing source code or talking about lessons learnt. Once again like most presentations at teched it would have been great to have a link at the end of the talk to any downloads/links etc. Talk had potential but didn't deliver.     &lt;br /&gt;&lt;em&gt;- That last sentence nicely sums up my feelings as well&lt;/em&gt;&lt;/li&gt;    &lt;li&gt;OK demo, but a bit too simple for business app.     &lt;br /&gt;&lt;em&gt;- I thought a title including “MMO” would indicate it wasn’t a business app.&amp;#160; Expectation management again.&lt;/em&gt;&lt;/li&gt;    &lt;li&gt;Session should have had more code examples going through the actual development process.&lt;/li&gt;    &lt;li&gt;The 4 speakers probably know the technology, but the whole session came across as a jumbled mess of in-jokes and non-seriousness. Not sure how anyone can manage to &amp;quot;learn&amp;quot; anything from these guys. Please plan the session better or if you really didn't have anything to say, just cancel it.     &lt;br /&gt;&lt;em&gt;- Preparation failure on our part.&amp;#160; I’m sorry.&lt;/em&gt;&lt;/li&gt;    &lt;li&gt;The humor and relavence of this session made it very worth while.     &lt;br /&gt;&lt;em&gt;- Humour works sometimes, but it didn’t work for everyone.&lt;/em&gt;&lt;/li&gt;    &lt;li&gt;The session was informative but I think that there the naming of it should have been better as the current title made the room uncomfortably crowded. The presenters were knowledgable in there fields but to me Steve Nagy was the best presenter, properly explaining the process he took in the development of that project. Though I don't mind some joking it really did distract too much from the amount of material that they needed to cover.&lt;/li&gt;    &lt;li&gt;the speakers did not realise the audience invested time to see this demo. They enjoyed each other company, but wasted my time.     &lt;br /&gt;&lt;em&gt;- Apologies for that.&amp;#160; It definitely wasn’t the intention.&lt;/em&gt;&lt;/li&gt;    &lt;li&gt;There was a little bit too much shenanigans on stage, and too many in jokes, so I think a lot in the audience lost interest. I think people were expecting to see more actual code and implementation (as the session title implied).&lt;/li&gt;    &lt;li&gt;This was a very ambitious session and unfortunately, the presenters did not quite pull it off. There was a lot of really cool stuff that these guts were trying to present - but the presentation was low on details, and the attempts at comedy took away from technology being presented&lt;/li&gt;    &lt;li&gt;Too busy trying to get a laugh from the crowd, that time could've been spent explaining some of the pitfalls they faced.&lt;/li&gt;    &lt;li&gt;too much focus on the &amp;quot;gaming concepts&amp;quot; (XNA/Latency&amp;amp;Prediction), but overall very good session.     &lt;br /&gt;&lt;em&gt;- A dry run would have helped balance a lot of the issues raised in the 3 comments above.&lt;/em&gt;&lt;/li&gt;    &lt;li&gt;Too much time was spent playing about on Stage and not enough on the content. A little bit is good but they went way too far. Points focused on seemed to be very specific to the problems they found with an MMO example, not necessarially translating to what someone else would experience building an app with a cloud backend for the three screen types. The third presenter was more straight up and he actually pulled the about scores up a bit. If I was rating him individually he would get far higher scores for the above, but I would have liked some deeper content, particularly from a 300 session. The one thing that would save this is if we could get a look at the code to actually see how much crossover there is between each of the clients in detail and pull it apart ourselves a bit.     &lt;br /&gt;&lt;em&gt;- I’m tossing up wether to put the code up on github to look at, but some of it was very rushed and not our best work.&amp;#160; Is that still useful?&amp;#160; Blog posts that go into detail may be better.&lt;/em&gt;&lt;/li&gt;    &lt;li&gt;Very good and informative session. Also the speakers' lightheart approach makes this sessions very interesting. Bravo&lt;/li&gt;    &lt;li&gt;Weirdos! Made the session fun     &lt;br /&gt;&lt;em&gt;- Erm, thanks for that summary… I think :-)&lt;/em&gt;&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;And there you have it.&amp;#160; A very public retrospective from my part on what went wrong, what could have been done better and the lessons to learn.&lt;/p&gt;  &lt;p&gt;Publicly talk about your failings is hard, yet there’s nothing that teaches you more than a big, public failure.&amp;#160; This is my attempt to do some tough learning in an open way so that hopefully you learn something from it as well.&lt;/p&gt;  &lt;p&gt;Finally, if you attended the session at and you thought it sucked, then I apologise and hope that you enjoyed the rest of the conference.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-4338694964048167336?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/4338694964048167336/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2011/09/learning-from-fail.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/4338694964048167336'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/4338694964048167336'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2011/09/learning-from-fail.html' title='Learning from a FAIL'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-2394908674364747264</id><published>2011-09-02T12:04:00.001+10:00</published><updated>2011-09-02T12:21:05.493+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><title type='text'>Using Parallel Task Library to Unit Test Threading Issues</title><content type='html'>I was doing some work recently on a demo application where data was being pulled in from multiple locations and being added to a collection that was also being iterated over in the same method.&amp;nbsp; Because this data was arriving on multiple threads (i.e. async network call backs for example) I’d occasionally see the usual “collection was modified” error messages indicating that another thread had altered the collection while the first was iterating over it.&amp;nbsp; Obvious threading bug, #FacePlam applied.&lt;br /&gt;&lt;br /&gt;While it can be complex at times to find these kinds of errors, in this case it was fairly easy to diagnose and fix, so following good bug fix practices I took the standard approach of writing a test to prove the bug exists, fixing the code and then running the test again to prove it’s fixed.&lt;br /&gt;&lt;br /&gt;Now, it should be noted that testing threading issues in a deterministic way is nigh on impossible, and there is no guarantee that a unit test for threading issues will genuinely prove the code bug free, however the approach taken here was good enough to throw the threading exception each and every time I ran the test and also the throw the exception on the build server.&lt;br /&gt;&lt;br /&gt;Here’s the code:&lt;br /&gt;&lt;br /&gt;&lt;div class="wlWriterEditableSmartContent" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:0d6b495d-8837-495e-b3a7-7b6cc666c082" style="display: inline; float: none; margin: 0px; padding: 0px;"&gt;&lt;pre class="brush: c#;"&gt;[TestMethod]&lt;br /&gt;public void ThreadingFun()&lt;br /&gt;{&lt;br /&gt;    InitializeControllerAndGroup();&lt;br /&gt;&lt;br /&gt;    Task[] tasks = new Task[10]&lt;br /&gt;                            {&lt;br /&gt;                                Task.Factory.StartNew(() =&amp;gt; MakeMove(1)),&lt;br /&gt;                                Task.Factory.StartNew(() =&amp;gt; MakeMove(2)),&lt;br /&gt;                                Task.Factory.StartNew(() =&amp;gt; MakeMove(1)),&lt;br /&gt;                                Task.Factory.StartNew(() =&amp;gt; MakeMove(2)),&lt;br /&gt;                                Task.Factory.StartNew(() =&amp;gt; MakeMove(1)),&lt;br /&gt;                                Task.Factory.StartNew(() =&amp;gt; MakeMove(2)),&lt;br /&gt;                                Task.Factory.StartNew(() =&amp;gt; MakeMove(1)),&lt;br /&gt;                                Task.Factory.StartNew(() =&amp;gt; MakeMove(2)),&lt;br /&gt;                                Task.Factory.StartNew(() =&amp;gt; MakeMove(1)),&lt;br /&gt;                                Task.Factory.StartNew(() =&amp;gt; MakeMove(2)),&lt;br /&gt;                            };&lt;br /&gt;    Task.WaitAll(tasks);&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;Ignore the first line, that’s just where the collection is being initialised.&amp;nbsp; Also ignore the fact that there’s no Assert statements in this code.&amp;nbsp; The test passes if we have no threading exceptions thrown and fails if we have one.&lt;br /&gt;&lt;br /&gt;The important thing here is to see how easy it is to fire off a lot of threads in a single, easy to read unit test without all the usual threading plumbing code that would litter something like this.&lt;br /&gt;&lt;br /&gt;The way it works is that we define a set of tasks via the Task Parallel Library (part of .NET 4.0) each of which calls the code where we have our threading problem.&amp;nbsp; When Task.Factory.StartNew() is called the Task Parallel Library (TPL) immediately creates a new thread and calls the method returning control to our code along with a Task object so would can check the state of the task or cancel it if so desired.&amp;nbsp; In this case we don’t care and immediately start another thread as soon as possible.&lt;br /&gt;&lt;br /&gt;We then use the Task.WaitAll statement to wait until all the Tasks we defined are completed so that the test doesn’t complete prematurely.&amp;nbsp; Too easy.&lt;br /&gt;&lt;br /&gt;Note that we could also just as easily have used Parallel.Invoke for this.&amp;nbsp; The same test using Parallel Invoke would be something like this:&lt;br /&gt;&lt;br /&gt;&lt;div class="wlWriterEditableSmartContent" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:fd79ed64-26d2-4934-bb6e-256d57868d3b" style="display: inline; float: none; margin: 0px; padding: 0px;"&gt;&lt;pre class="brush: text;"&gt;[TestMethod]&lt;br /&gt;public void ParallelInvoke()&lt;br /&gt;{&lt;br /&gt;    InitializeControllerAndGroup();&lt;br /&gt;&lt;br /&gt;    Parallel.Invoke(&lt;br /&gt;        () =&amp;gt; MakeMove(1),&lt;br /&gt;        () =&amp;gt; MakeMove(2),&lt;br /&gt;        () =&amp;gt; MakeMove(1),&lt;br /&gt;        () =&amp;gt; MakeMove(2),&lt;br /&gt;        () =&amp;gt; MakeMove(1),&lt;br /&gt;        () =&amp;gt; MakeMove(2),&lt;br /&gt;        () =&amp;gt; MakeMove(1),&lt;br /&gt;        () =&amp;gt; MakeMove(2),&lt;br /&gt;        () =&amp;gt; MakeMove(1),&lt;br /&gt;        () =&amp;gt; MakeMove(2)&lt;br /&gt;    );&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;I personally prefer the first approach because I like the more explicit control over the thread creation, though it’s obviously noisier than the Parallel.Invoke version.&amp;nbsp; Note that with Parallel.Invoke you hand over control to the TPL and it figures out how many threads it will use to run the actions you define based on the number of cores available on the machine.&lt;br /&gt;&lt;br /&gt;Regardless of the method you choose you can take advantage of the TPL to help you unit test your multithreaded code and make your application more resilient.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-2394908674364747264?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/2394908674364747264/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2011/09/using-parallel-task-library-to-unit.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/2394908674364747264'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/2394908674364747264'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2011/09/using-parallel-task-library-to-unit.html' title='Using Parallel Task Library to Unit Test Threading Issues'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-8791484328388951114</id><published>2011-08-31T16:22:00.003+10:00</published><updated>2011-08-31T16:24:33.566+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='WP7'/><title type='text'>How To Run Multiple WP7 Emulators</title><content type='html'>The Windows Phone 7 Tools, including the latest Mango update, provide no supported way for running two emulators at the same time.&amp;nbsp; This can be a problem when trying to see how a multiuser application works across multiple devices.&amp;nbsp; Fortunately there’s a way to workaround this limitation using some unsupported edits to your WP7 emulator settings.&lt;br /&gt;NOTE: This is not a supported change and if something breaks on your machine, well… you’ve been warned :-)&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; Open explorer in Administrator mode and go to C:\ProgramData\Microsoft\Phone Tools\CoreCon\10.0\addons&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; Copy “ImageConfig.en-US.xsl” and name it “ImageConfig.en-US 2.xsl”&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Step 3:&lt;/strong&gt; Open this new file in your text editor of choice and locate the DEVICE element and change the device name to something of your choice and the device GUID to a new GUID as shown.&amp;nbsp; Use the &lt;a href="http://www.guidgenerator.com/online-guid-generator.aspx"&gt;Online GUID Generator&lt;/a&gt; to make generating new GUIDs easy.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://lh3.ggpht.com/-Y6XVbFBh9-o/Tl3THeGcWFI/AAAAAAAABM8/lWkHvnLBuJE/s1600-h/image%25255B5%25255D.png"&gt;&lt;img alt="image" border="0" height="59" src="http://lh5.ggpht.com/-LeJnomTpB4o/Tl3TIJYnHnI/AAAAAAAABNA/STgqFF_j3Z4/image_thumb%25255B3%25255D.png?imgmax=800" style="background-image: none; border: 0px currentColor; display: inline; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="image" width="644" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Step 4:&lt;/strong&gt; Given the WP7 is a virtual machine, we also need to supply a new Virtual Machine ID, so locate the VMID section and provide a new GUID in there as well.&amp;nbsp; Again the &lt;a href="http://www.guidgenerator.com/online-guid-generator.aspx"&gt;Online GUID Generator&lt;/a&gt; can be used for this purpose.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://lh3.ggpht.com/-aD8999WBvk4/Tl3TI4k6S6I/AAAAAAAABNE/4kayd9OelVw/s1600-h/image%25255B9%25255D.png"&gt;&lt;img alt="image" border="0" height="96" src="http://lh4.ggpht.com/-jo93aVk8gkk/Tl3TJgVYgNI/AAAAAAAABNI/1Q5G50tIqWo/image_thumb%25255B5%25255D.png?imgmax=800" style="background-image: none; border: 0px currentColor; display: inline; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="image" width="449" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Ensure that the GUID you insert is in the same format (i.e. with the braces)&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Step 5:&lt;/strong&gt;&amp;nbsp; Save the file and start the Application Deployment Tool.&amp;nbsp; You should now see your new emulator available to deploy your applications to:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://lh3.ggpht.com/-pL06EaDHflI/Tl3TKPIpBDI/AAAAAAAABNM/D9Itd4svlrM/s1600-h/image%25255B13%25255D.png"&gt;&lt;img alt="image" border="0" height="157" src="http://lh4.ggpht.com/-XDtwACAVGN0/Tl3TKxrnN1I/AAAAAAAABNQ/gFFd2hxx4gU/image_thumb%25255B7%25255D.png?imgmax=800" style="background-image: none; border: 0px currentColor; display: inline; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="image" width="450" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;If you close and restart Visual Studio 2010 you should also seen the second phone emulator available as a deployment target:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://lh4.ggpht.com/-jkEii2kzIPI/Tl3TLQc5U8I/AAAAAAAABNU/PsjVW8R7cOs/s1600-h/image%25255B17%25255D.png"&gt;&lt;img alt="image" border="0" height="119" src="http://lh6.ggpht.com/-FdXurjhNAKE/Tl3TLwKZU4I/AAAAAAAABNY/xi0AOLFPf7k/image_thumb%25255B9%25255D.png?imgmax=800" style="background-image: none; border: 0px currentColor; display: inline; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="image" width="252" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Note that of you run a phone application in debug mode there seems to be no way to launch a second instance on the other emulator whilst the phone is running.&amp;nbsp; It’s not a major problem though since you can simply take the debug .xap file and deploy it to the second emulator using the deployment tool.&lt;br /&gt;&lt;br /&gt;I hope this helps you out.&amp;nbsp; Enjoy!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-8791484328388951114?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/8791484328388951114/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2011/08/how-to-run-multiple-wp7-emulators.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/8791484328388951114'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/8791484328388951114'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2011/08/how-to-run-multiple-wp7-emulators.html' title='How To Run Multiple WP7 Emulators'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/-LeJnomTpB4o/Tl3TIJYnHnI/AAAAAAAABNA/STgqFF_j3Z4/s72-c/image_thumb%25255B3%25255D.png?imgmax=800' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-404428876910483906</id><published>2011-08-19T11:39:00.002+10:00</published><updated>2011-08-19T11:40:54.300+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='presentations'/><category scheme='http://www.blogger.com/atom/ns#' term='community'/><title type='text'>Getting Out of Sync With IIS and Riding A Comet</title><content type='html'>It’s taken a little while but my slide deck from &lt;a href="http://dddsydney.com/"&gt;DDD Sydney, 2011&lt;/a&gt; is now up on the web.&lt;br /&gt;Unfortunately I don’t have the code or load test details embedded in the presentation but even so this should still have a number of useful things for you to look at, especially the resources section at the end &lt;img alt="Smile" class="wlEmoticon wlEmoticon-smile" src="http://lh5.ggpht.com/-bnE5GBpp0D0/Tk2-vPFrCCI/AAAAAAAABM4/T7dPot8kaB8/wlEmoticon-smile%25255B2%25255D.png?imgmax=800" /&gt;&lt;br /&gt;Oh, as an experiment, I’ve put both a &lt;a href="http://www.slideshare.net/"&gt;Slideshare&lt;/a&gt; player and a &lt;a href="http://office.microsoft.com/en-gb/powerpoint/embed-a-powerpoint-presentation-on-a-webpage-FX102602487.aspx"&gt;Powerpoint Web App&lt;/a&gt; player in this post.&amp;nbsp; I’d like to know which one you think is better (send me a tweet to @rbanks54 or leave a comment)&lt;br /&gt;&lt;iframe frameborder="0" height="327" scrolling="no" src="http://r.office.microsoft.com/r/rlidPowerPointEmbed?p1=1&amp;amp;p2=1&amp;amp;p3=SDEF4A085C562CD5AD!172&amp;amp;p4=&amp;amp;kip=1" width="402"&gt;&lt;/iframe&gt;  &lt;br /&gt;&lt;div id="__ss_8910545" style="width: 425px;"&gt;&lt;strong style="display: block; margin: 12px 0px 4px;"&gt;&lt;a href="http://www.slideshare.net/rbanks54/ddd-sydney-2011-getting-out-of-sync-with-iis-and-riding-a-comet" title="DDD Sydney 2011 - Getting out of Sync with IIS and Riding a Comet"&gt;DDD Sydney 2011 - Getting out of Sync with IIS and Riding a Comet&lt;/a&gt;&lt;/strong&gt;&lt;object height="355" id="__sse8910545" width="425"&gt;&lt;param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=asyncasp-netembeddedfonts-110818202937-phpapp01&amp;amp;stripped_title=ddd-sydney-2011-getting-out-of-sync-with-iis-and-riding-a-comet&amp;amp;userName=rbanks54" /&gt;&lt;param name="allowFullScreen" value="true" /&gt;&lt;param name="allowScriptAccess" value="always" /&gt;&lt;embed name="__sse8910545" src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=asyncasp-netembeddedfonts-110818202937-phpapp01&amp;amp;stripped_title=ddd-sydney-2011-getting-out-of-sync-with-iis-and-riding-a-comet&amp;amp;userName=rbanks54" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"&gt;&lt;/embed&gt;&lt;/object&gt;    &lt;br /&gt;&lt;div style="padding: 5px 0px 12px;"&gt;View more &lt;a href="http://www.slideshare.net/"&gt;presentations&lt;/a&gt; from &lt;a href="http://www.slideshare.net/rbanks54"&gt;Richard Banks&lt;/a&gt;.&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-404428876910483906?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/404428876910483906/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2011/08/getting-out-of-sync-with-iis-and-riding.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/404428876910483906'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/404428876910483906'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2011/08/getting-out-of-sync-with-iis-and-riding.html' title='Getting Out of Sync With IIS and Riding A Comet'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/-bnE5GBpp0D0/Tk2-vPFrCCI/AAAAAAAABM4/T7dPot8kaB8/s72-c/wlEmoticon-smile%25255B2%25255D.png?imgmax=800' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-6541974582173946273</id><published>2011-08-12T15:49:00.001+10:00</published><updated>2011-08-12T15:49:29.814+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='agile'/><category scheme='http://www.blogger.com/atom/ns#' term='management'/><title type='text'>Outsourcing, Unit Testing and Costs</title><content type='html'>&lt;p&gt;Let’s say you work in an internal IT department, largely doing maintenance work on software solutions.&amp;#160; Solutions that are by and large built by external vendors through an outsourcing arrangement and then brought in house once they’ve gone live and the initial warranty period has elapsed.&lt;/p&gt;  &lt;p&gt;Let’s say every time you look at one of these solutions that you now have to maintain you find the grand sum of zero unit tests.&amp;#160; Without exception. Every single application has nary a unit test to be seen. Not even a hint that one might have existed and been removed at a later date.&lt;/p&gt;  &lt;p&gt;“Why?!” you cry to yourself.&amp;#160; In frustration you cry “Why!!?” to management as well.&amp;#160; Their answer: The vendors tell us “it will cost more and take longer to do unit testing” and of course, they reason with you, if that’s the case then why would we pay for it? You know this is a bad argument and the logic is built on sand, so what’s your comeback? How do you get it across to management that a vendor that ignores unit testing is a bad vendor and is actually more expensive overall?&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;So my initial response to their claims would be along the lines of “So do I assume they’re not actually testing the software they write for us then.” and then explain how manual testing takes (much) longer than developing automated tests, assuming of course that the code being written is in any way testable.&amp;#160; If they develop poorly and have a lot of tight coupling between concrete classes or make a lot of static calls to HttpContext or develop in SharePoint for instance then the application might not be easily testable, but hey! That’s their problem not ours. The good news is that anyone with a business brain can understand the benefits of automation for tedious repetitive time consuming processes – heck, we worked that out during the industrial revolution.&lt;/p&gt;  &lt;p&gt;Let’s try a different angle in case the first, most obvious one doesn’t work.&amp;#160; If you consider a lack of automated testing to be merely another form of technical debt then it’s just like poor design and architecture, horribly complex methods, N+1 ORM queries, poor UI’s, high bug counts, etc. It’s something that will cost you. So, if a vendor delivers you software with a high level of technical debt and you’ll be maintaining it, then you’re going to have a lower long term ROI and higher TCO from those vendors versus what you might have from a vendor who keeps their debt levels low.&amp;#160; So while the cheap guys may argue that it will cost more and takes longer to write code with unit tests then what they’re really saying is this&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;“It costs &lt;em&gt;US&lt;/em&gt; more and takes &lt;em&gt;US&lt;/em&gt; longer to do unit testing because the developers we hire are the cheapest and suckiest we could find. They don’t understand good development practices and we’re not about to teach them because they’ll leave in the next 3 to 6 months. Also manual testers here are cheap-as-chips and lose staff too quickly, so it’s easier for US if we just get the software to ‘it kind of works’ and then give it to you so we can get our money and hope you won’t notice”.&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;Also, don’t forget that post delivery, all those bugs and problems that snuck through from the vendor will end up becoming your bugs and problems to fix as part of your maintenance work.&amp;#160; So the company is either paying for it with internal staff costs (often seen as a hidden/sunk cost) or, if the vendor is also charging for maintenance, paying for it once a development project is concluded.&amp;#160; Development takes longer, it’s harder to find and fix bugs, it takes longer and longer to test the application manually, etc.&amp;#160; These all add up to time, and in software development time is money!&lt;/p&gt;  &lt;p&gt;With that, good luck convincing management to change their vendors, and if you’ve got a better reason to present yours case I’d love to hear it.&lt;/p&gt;  &lt;p&gt;Note: I’m not implying manual testing is bad.&amp;#160; In fact I think you should be doing manual testing; but only manual exploratory testing where testers are trying to break your code.&amp;#160; It’s rote, repetitive manual testing that is neither cost efficient or effective.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-6541974582173946273?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/6541974582173946273/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2011/08/outsourcing-unit-testing-and-costs.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/6541974582173946273'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/6541974582173946273'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2011/08/outsourcing-unit-testing-and-costs.html' title='Outsourcing, Unit Testing and Costs'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-8559316941776935351</id><published>2011-08-08T10:30:00.001+10:00</published><updated>2011-08-08T10:30:48.086+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='presentations'/><category scheme='http://www.blogger.com/atom/ns#' term='tech.ed'/><title type='text'>Going to TechEd Australia? Lock in DEV305</title><content type='html'>&lt;p&gt;Why? Because that’s the session you want to go to of course!&lt;/p&gt;  &lt;p&gt;Here’s the link with all the details: &lt;a href="http://australia.msteched.com/topic/details/DEV305"&gt;http://australia.msteched.com/topic/details/DEV305&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://www.aaron-powell.com/"&gt;Aaron Powell&lt;/a&gt;, &lt;a href="http://lzcd.com/"&gt;Luke Drumm&lt;/a&gt;, &lt;a href="http://azure.snagy.name/blog/"&gt;Steve Nagy&lt;/a&gt; and myself are going to show you how to build applications for the cloud using multiple clients (i.e. PC and Windows Phone 7 in this case) and some of the things to watch out for.&amp;#160; Don’t worry it won’t be one of those sessions where people try and prove how awesome they are by writing the whole thing on stage.&amp;#160; Instead we’ll be showing you the highlights and what you should think about if you want to try something similar.&lt;/p&gt;  &lt;p&gt;I’ll also have my recording equipment with me and will be recording a number of podcasts for &lt;a href="http://www.talkingshopdownunder.com/"&gt;Talking Shop Down Under&lt;/a&gt; with people while I’m up there, so if you want to hear from someone in particular, let me know so I can line up a time with them!&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-8559316941776935351?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/8559316941776935351/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2011/08/going-to-teched-australia-lock-in.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/8559316941776935351'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/8559316941776935351'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2011/08/going-to-teched-australia-lock-in.html' title='Going to TechEd Australia? Lock in DEV305'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-7404832274166389839</id><published>2011-07-05T11:35:00.001+10:00</published><updated>2011-07-05T11:35:47.785+10:00</updated><title type='text'>True Community is What YOU Make it</title><content type='html'>&lt;p&gt;Roy Osherove’s post on &amp;quot;&lt;a href="http://7enn.com/2011/07/04/appreciating-the-power-of-a-true-community/"&gt;True Community&lt;/a&gt;”™ has been making the rounds on the twitterz in the last 24 hours (or as some people have called it “Yet Another Leaving .NET Post”).&amp;#160; I feel like I should respond with some thoughts.&lt;/p&gt;  &lt;p&gt;First up, let me say I applaud Roy for making his views clear and talking about the differences in the communities and the speed at which the Ruby/Rails community is driving forward.&amp;#160; I thinks it great that someone showed enough courage to switch from one technology to another and begin the learning process all over again.&amp;#160; What I have a problem with is the deriding of the technology/community you leave.&amp;#160; It’s a pretty simple reason too.&lt;/p&gt;  &lt;p&gt;The Ruby/Rails community and the .NET community are &lt;em&gt;different&lt;/em&gt; communities!&amp;#160; There’s no such thing as “True Community”, just community.&amp;#160; Calling your community anything else is simply hyperbole, elitism and idealism.&lt;/p&gt;  &lt;p&gt;Of course the two communities won’t be the same! They’re made up of different people. Neither will one community be inherently better than another.&amp;#160; They are just made up of different people.&amp;#160; An apple is not inherently better than an orange or a banana, just because it’s a different fruit.&amp;#160; And just because you’ve decided that you like your new community because it’s more akin to your personal tastes, doesn’t mean that those in the other community are ignoramuses, fools, ill-treated and misguided or troglodytes.&amp;#160; These sort of attitudes won’t and don’t help anyone.&lt;/p&gt;  &lt;p&gt;We’re all developers and we all want to make the best software we can using whatever our chosen technology is.&amp;#160; If you don’t like the way your technology or community is progressing or you like the look of other groups more and you leave, that’s fine.&amp;#160; Go for it. Be awesome no matter where you go.&amp;#160; However if you do leave, don’t then berate those who stay or point out only the negatives as you see them.&amp;#160; Constructive criticism is welcome.&amp;#160; Criticism that exists just to validate your decision to move is not.&lt;/p&gt;  &lt;p&gt;The Ruby/Rails community is still quite small and young and thus is able to change rapidly and innovate quickly.&amp;#160; It won’t last forever, it never does, but there’s a lot to like about a community like that, and a lot that everyone can learn from it. I get that. Truly I do.&amp;#160; I wish there was more of that going on in the .NET space.&amp;#160; Look at the Java and .NET communities on the other hand. They’re much larger and more prone to innovation impedance, making it harder to change things and for .NET in particular when management places more focus on what Microsoft produces to the exclusion of better solutions there can be some real problems.&lt;/p&gt;  &lt;p&gt;On the flip side, while Roy makes some strong points about Microsoft not valuing the community, I think they only apply to certain parts of the business.&amp;#160; It’s not like that everywhere, and it’s changing.&amp;#160; I was on an open source panel on the weekend at the DDD Sydney conference and stated that we’re seeing a gradual change of Microsoft’s attitude towards their open source developers and the developer community in general.&amp;#160; The move from closed source to grudging acceptance of open source to a willing approval and active contribution to open source in the community at large has been a slow transition, but it is happening.&amp;#160; Why? Because of the very community that Roy complains about is driving change! Groups like Alt.NET that are looking for ways to push things forward regardless of what Redmond says do have an influence.&amp;#160; We see the change because of outspoken and community focused people like Roy himself, and others like Ayende Rahien, Sebastian Lambla, Jeremy Miller, the Herding Code guys, the Microsoft MVC team (who have strong community roots) and many, many more.&lt;/p&gt;  &lt;p&gt;People that realise that community is what &lt;em&gt;you&lt;/em&gt; make it!&lt;/p&gt;  &lt;p&gt;So, you don’t like something in a community you’re in? Then what are you doing to improve it.&amp;#160; If the answer is “nothing”, then either stop complaining or get off your butt and get active.&amp;#160; It’s pretty simple really.&amp;#160; Change your community or change your community.&lt;/p&gt;  &lt;p&gt;On a side note, the keen eyed amongst you will notice that Roy’s post didn’t mention the JavaScript community? Why? There’s just as much innovation and excitement happening in that space, if not more.&amp;#160; What makes them different? Why don’t we see “I’m leaving .NET for JavaScript” posts? Is it because their community is inclusive by nature and the Rails community is elitist? Is it because the technology is server side agnostic? Is it something else?&amp;#160; You know what.&amp;#160; Who cares! It’s just another community.&amp;#160; One from which everyone can learn.&lt;/p&gt;  &lt;p&gt;Nothing says you need to be exclusive.&amp;#160; You like .NET, Visual Studio &amp;amp; IntelliSense?&amp;#160; Wonderful.&amp;#160; Go contribute to your local .NET community.&amp;#160; You like Ruby and the Rails framework and enjoy the innovation in that space? Fantastic. Go contribute to your local Rails community.&amp;#160; You like Java and think there’s still a lot of improving that can be done there? Brilliant. Go contribute to your local Java community.&amp;#160; You like JavaScript and think modern browsers have the power you finally need to make some kick ass software? Excellent. Go contribute to your local JavaScript community.&lt;/p&gt;  &lt;p&gt;You get the point.&amp;#160; The community is made of &lt;em&gt;you&lt;/em&gt; so go influence your community.&lt;/p&gt;  &lt;p&gt;Fin.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-7404832274166389839?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/7404832274166389839/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2011/07/true-community-is-what-you-make-it.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/7404832274166389839'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/7404832274166389839'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2011/07/true-community-is-what-you-make-it.html' title='True Community is What YOU Make it'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-1429317977980111944</id><published>2011-07-04T21:13:00.001+10:00</published><updated>2011-07-04T21:13:45.804+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='community'/><category scheme='http://www.blogger.com/atom/ns#' term='general'/><title type='text'>My Geek Origin Story, what’s yours?</title><content type='html'>&lt;p&gt;&lt;em&gt;Yay! It’s Meme time, people.&amp;#160; This one started by &lt;/em&gt;&lt;a href="http://twitter.com/delic8genius"&gt;&lt;em&gt;@delic8genius&lt;/em&gt;&lt;/a&gt;&lt;em&gt; (&lt;a href="http://delicategeniusblog.com/"&gt;Michael Kordahi&lt;/a&gt;) who wants to learn your &lt;/em&gt;&lt;a href="http://delicategeniusblog.com/?p=1292"&gt;&lt;em&gt;Geek Origin Story&lt;/em&gt;&lt;/a&gt;&lt;em&gt; and see how we all got in touch with our inner geek.&amp;#160; So here’s mine…&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;For me I remember it all starting with the &lt;a href="http://en.wikipedia.org/wiki/Atari_2600"&gt;Atari 2600&lt;/a&gt; and a little game called &lt;a href="http://au.gamespot.com/atari2600/action/asteroids/index.html"&gt;Asteroids&lt;/a&gt;, though looking back at photos from my earlier years I can see that the Atari was merely the trigger that brought the latent geek in me to the surface. I mean seriously, look at this photo of me as a little tacker and just ask yourself if that’s not a geek waiting to happen!&amp;#160; And no, that’s not a laptop backpack I’m wearing, though it could be! :-)&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/-DB_DpANabhs/ThGgZBJTupI/AAAAAAAABH0/mt3J6hRw_yM/s1600-h/Mini%252520Richard%25255B3%25255D.jpg"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="Mini Richard" border="0" alt="Mini Richard" src="http://lh3.ggpht.com/-0Dt_ChE8VgI/ThGgaLF1WSI/AAAAAAAABH4/S-rt-Vrh30o/Mini%252520Richard_thumb%25255B1%25255D.jpg?imgmax=800" width="233" height="240" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;I spent days and weeks blowing up asteroids in space and leaping through jungles, swinging on vines and jumping over pits (&lt;a href="http://en.wikipedia.org/wiki/Pitfall!"&gt;Pitfall&lt;/a&gt; anyone?!), but that all faded to backstory when I went to a selective school and my parents decided it was good for my education if they splashed out on a &lt;a href="http://en.wikipedia.org/wiki/Commodore_64"&gt;Commodore 64&lt;/a&gt;.&amp;#160; The day that thing came home, my fate was sealed.&amp;#160; As a kid in primary school I remember the unboxing, plugging it into the TV along with the &lt;a href="http://en.wikipedia.org/wiki/Commodore_Datassette"&gt;tape drive&lt;/a&gt; and an &lt;a href="http://en.wikipedia.org/wiki/Commodore_1541"&gt;external floppy drive&lt;/a&gt;, turning it on and then seeing the blue screen, the blinking cursor and the READY prompt.&amp;#160; What now?! We could LOAD something from tape or disk, or we could crack out the programming books that it came with and explore the possibilities!&amp;#160; The games were great, but being able to type things into that C64 and watch them run was a revelation! Not only could I play games, and oh! how I played games, but I could write them as well!&amp;#160; I remember writing my own text based adventure games (they were crap of course) and building programs that would show animated running man sprites moving across the screen based on how the joystick was pushed.&amp;#160; It was a marvel, pure and simple.&amp;#160; Being able to make the computer do what I wanted based on my decisions and actions and see the results on screen.&amp;#160; Mwahahaha! The power! The unlimited, unfettered POWER!!! *Cough* *&lt;b&gt;Ahem&lt;/b&gt;* Yes, what was I saying? I think I got carried away with myself there…&lt;/p&gt;  &lt;p&gt;&lt;img src="http://www.c64-wiki.com/images/c/c9/Einschaltmeldung_C64.jpg" /&gt;&lt;/p&gt;  &lt;p&gt;So I’m sure my parents wished I would go play sport with the other kids on the street more often than I did, but I knew I’d never be great at that.&amp;#160; Computers however? Different story.&amp;#160; I controlled the universe there.&amp;#160; They made sense to me.&amp;#160; I could figure out how it all fit together.&amp;#160; I could &lt;a href="http://en.wikipedia.org/wiki/PEEK_and_POKE"&gt;PEEK and POKE&lt;/a&gt; with the best of them.&amp;#160; I got so much joy, fun, fulfilment and square eyes from playing games and writing software that I knew what I wanted to do for the rest of my life.&amp;#160; You see, I’m a gamer, through and through.&amp;#160; I love playing games and programming is just another form of game.&amp;#160; A different form of accomplishment, but still the same sensation that you get from finishing a level or beating a boss fight.&amp;#160; Tell me you haven’t got some code working at times and fist pumped, or put your arms in the air in a victory pose! I’m sure this is how half of the world’s programmers started their coding careers – wishing they could spend all their time playing and writing games as cool and puzzling as &lt;a href="http://en.wikipedia.org/wiki/Boulder_Dash"&gt;Boulder Dash&lt;/a&gt; or &lt;a href="http://en.wikipedia.org/wiki/Impossible_Mission"&gt;Impossible Mission&lt;/a&gt; or &lt;a href="http://en.wikipedia.org/wiki/Paradroid"&gt;Paradroid&lt;/a&gt;.&amp;#160; I’m no different.&lt;/p&gt;  &lt;p&gt;So there you have it.&amp;#160; My geek origin story, what’s yours?&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-1429317977980111944?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/1429317977980111944/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2011/07/my-geek-origin-story-whats-yours.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/1429317977980111944'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/1429317977980111944'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2011/07/my-geek-origin-story-whats-yours.html' title='My Geek Origin Story, what’s yours?'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/-0Dt_ChE8VgI/ThGgaLF1WSI/AAAAAAAABH4/S-rt-Vrh30o/s72-c/Mini%252520Richard_thumb%25255B1%25255D.jpg?imgmax=800' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-6460665339457804467</id><published>2011-06-20T16:31:00.002+10:00</published><updated>2011-06-20T16:31:54.140+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='agile'/><title type='text'>Agile Australia: A Retrospective</title><content type='html'>&lt;br /&gt;Last week I was a panellist at the Agile Australia conference, talking about hiring people for agile teams, and whilst that was fun the conference overall left me feeling cold, frustrated and disappointed.  Anyone who saw my tweet stream during the conference would have probably felt some of that and now that I’ve calmed down a little and thought about things some more, I think it’s time to get to the heart of what really annoyed me and offer some suggestions as to what to do next time.  Consider this constructive criticism.&lt;br /&gt;&lt;br /&gt;Firstly, it felt like conference sponsors were given slots based on the amount of sponsorship they paid.  The title sponsor had the largest number of slots and each of the gold sponsors also appeared to have the option to take slots.  The problem I have with this is that a staff member from the title sponsor was also part of the advisory panel, as were staff from their major customers.  Conflict of interest anyone? What we saw in the conference were “case study” sessions presented by large corporates who just happened to be customers of the sponsor or sessions from colleagues of other advisory board members.  It meant that the event became largely a marketing exercise targeting other large corporates wrapped in conference clothing with very little to offer for anyone working in small organisations or who is already practicing agile and looking for ways to improve through learning from others.  The worst session was one where the presenter decided to slowly read a carefully prepared, PR laden, speech.  No slides, no audience engagement, just fluff.  That sort of thing is simply unforgivable, and even now still gets by blood boiling!&lt;br /&gt;&lt;br /&gt;Given one of the key principles of being agile is having the courage to be open and visible with the process I can safely say that this was not an approach followed by the conference.  What I would like to see? Community voting on topics, with all submitted sessions available and open for the public/attendees to vote on.  If sponsors are getting a number of slots then indicate how many by blanking out their sessions on the schedule, that way we can also see just how much non-sponsor content we may get.&lt;br /&gt;&lt;br /&gt;Next on my gripe list. Most sessions were targeted at beginners with content rarely reaching the ‘200-level’ let along going beyond that into more advanced and deeper topics.  I’m fine with a number of sessions being beginners based, but to have most of the conference aimed at that level? Urgh!  At least mark the sessions with some sort of required knowledge levelling system.  It’s important to have sessions marked as “best for beginners”, but please, not the whole conference!&lt;br /&gt;&lt;br /&gt;Moving on from that, there was also very little opportunity for ad-hoc discussion with other attendees.  The “Individuals and Interactions” aspect of agile should apply to conferences too.  Now there were a number of panel sessions, but a panel is nowhere near as interactive as an open space, and asking questions in a room of 200 is much harder for most people than in a group of 20-30.  The only open space time in the conference was a 45 minute session at the back end of day 2.  This, for me, was far and away the best part of the conference as it meant I could finally sit down and discuss problems and issues with other people and bounce some ideas around, but because of time limitations we really only had 15 minutes per topic. This is just enough time to really get started in on a topic but not enough to get far beyond the shallows.  Frustrating!  I’d like to see an entire day put aside for open space topics.  Make day 1 a “fixed content” day and day 2 an open spaces and workshop day so that we can really dig into things and learn from each other.&lt;br /&gt;&lt;br /&gt;Finally, let’s talk about the keynotes. 2 of the 4 were keynotes were good, though the content was unfortunately recycled. The other 2 sessions were terrible.  One featured 40 minutes of name dropping, self promotion, marketing and trite stories without any real content at all, and the other was a cliché-ridden, pandering, hippy themed “lets all love each other because we’re all geniuses!” farce.  Why a 2 day conference needs 3 keynotes and 2 lock notes is beyond me.  I want less of that nonsense and more content next time, just not more toddlers level content!&lt;br /&gt;&lt;br /&gt;And if you’re wondering if I would recommend you attend next years Agile Australia conference, and things don’t improve dramatically, then here’s my answer: If you work in a large corporate, are new to agile and you’re still trying to figure out how to make it work for you then yes, but take your marketing proof earmuffs with you.  If that’s not you, then stay well away.  It’s a complete and utter waste of time.&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-6460665339457804467?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/6460665339457804467/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2011/06/agile-australia-retrospective.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/6460665339457804467'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/6460665339457804467'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2011/06/agile-australia-retrospective.html' title='Agile Australia: A Retrospective'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-5581203007374193457</id><published>2011-06-06T20:59:00.001+10:00</published><updated>2011-06-06T20:59:26.750+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='presentations'/><category scheme='http://www.blogger.com/atom/ns#' term='community'/><title type='text'>DDD Sydney, 2011 – Saturday, July 2nd</title><content type='html'>&lt;p&gt;Are you an Aussie dev? Based in Sydney or somewhere within driving/flying distance?&lt;/p&gt;  &lt;p&gt;Well, why don’t you get yourself along to DDD Sydney this year? Not only will you have a chance to hear me wax lyrical about Comet&amp;#160; with IIS and how async coding can help improve performance, but you’ll also get to partake of a slew, nay a plethora!, of topics from other great speakers.&lt;/p&gt;  &lt;p&gt;Apart from my talk, I’ll also be a panellist for the Open Source panel, and I’m facilitating the Web Panel later in the day, so at least you now know which sessions to avoid, right? :-)&lt;/p&gt;  &lt;p&gt;Jokes aside, last years DDD Sydney was a lot of fun and this year will be even bigger – the &lt;a href="http://www.dddsydney.com/agenda.aspx"&gt;agenda&lt;/a&gt; and &lt;a href="http://www.dddsydney.com/Registration.aspx"&gt;registration&lt;/a&gt; are now available.&amp;#160; Go check out the details, book yourself a ticket, and I’ll see you there!&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-5581203007374193457?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/5581203007374193457/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2011/06/ddd-sydney-2011-saturday-july-2nd.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/5581203007374193457'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/5581203007374193457'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2011/06/ddd-sydney-2011-saturday-july-2nd.html' title='DDD Sydney, 2011 – Saturday, July 2nd'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-3734500665633532487</id><published>2011-06-06T10:57:00.001+10:00</published><updated>2011-06-06T10:57:37.141+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tools'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='.net'/><title type='text'>Anaesthetic for your #region-itis</title><content type='html'>&lt;p&gt;If your code (or the rest of your team) suffers from a bad case of #region-itis then help is at hand.&lt;/p&gt;  &lt;p&gt;There’s a lovely little Visual Studio extension over at &lt;a href="http://teamsearchapp.com/region-tool"&gt;http://teamsearchapp.com/region-tool&lt;/a&gt; that will automatically expand #regions, and also make those #region lines much smaller and harder to read than your normal code.&amp;#160; No more pressing shortcuts to expand all those blocks, less visual clutter and optionally, the ability to prevent collapsing of #regions.&amp;#160; What’s not to like?&lt;/p&gt;  &lt;p&gt;Borrowing a few pictures from their site, it takes the #region afflicted code (on the left) and reveals it’s ugly inner truth (on the right):&lt;/p&gt;  &lt;p&gt;&lt;img alt="Regions1" src="http://teamsearchapp.com/images/regions/Regions1.png?1306892825" /&gt;&amp;#160;&lt;img alt="Regions2" src="http://teamsearchapp.com/images/regions/Regions2.png?1306892825" /&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;Ahh! That’s much better.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-3734500665633532487?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/3734500665633532487/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2011/06/anaesthetic-for-your-region-itis.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/3734500665633532487'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/3734500665633532487'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2011/06/anaesthetic-for-your-region-itis.html' title='Anaesthetic for your #region-itis'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-3097634656806340966</id><published>2011-05-17T11:55:00.001+10:00</published><updated>2011-05-17T11:55:40.321+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='TFS'/><title type='text'>What’s Coming with TFS vNext</title><content type='html'>&lt;p&gt;Today at Tech.Ed US, Jason Zander and co. went through some of what’s coming in the next versions of TFS and Visual Studio and then &lt;a href="http://blogs.msdn.com/b/jasonz/archive/2011/05/16/announcing-alm-roadmap-in-visual-studio-vnext-at-teched.aspx"&gt;blogged about it&lt;/a&gt; shortly after.&lt;/p&gt;  &lt;p&gt;I saw some of this in action at the MVP summit earlier this year and I liked what I saw, so I’m very glad to see this being publicly talked about now. Now I could recap everything said already, but instead of regurgitating what’s on Jason’s blog, let me quickly call out some of the highlight that I’m interested in, and then you can go to the post, check out all the screenshots, and &lt;a href="http://go.microsoft.com/?linkid=9772730"&gt;look at the whitepaper&lt;/a&gt; to read more, and see even more screen shots.&lt;/p&gt;  &lt;p&gt;This upcoming release aims to close the loop between the business and the developers by building requirements management and end user feedback into the tooling.&amp;#160; For most software development projects, timely interaction with the customer is a critical element for success and having software that supports this is fantastic.&amp;#160; It also extends the communication loop from developers through to the ops team so that performance problems, bugs with stack traces and other issues that IT Pros find in production can be pushed back directly to the team to fix.&amp;#160; With my agile coaching hat on, I’m really looking forward to getting teams and organisations using these features to remove waste and improve what they do.&lt;/p&gt;  &lt;p&gt;PowerPoint will be enabled for use as a lightweight requirements and wire framing tool.&amp;#160; This is sooo much more approachable than Sketchflow and given it follows what a lot of people already do, this is a very welcome addition.&amp;#160; Customers, BA’s, and others that want to quickly use PowerPoint to knock out a wireframe of how an application might work can now do so and incorporate it into TFS and make it part of the requirements management process.&amp;#160; Excellent!&lt;/p&gt;  &lt;p&gt;TFS will also feature an agile task board and provide tools to make backlog management and prioritisation easier.&amp;#160; No more going to excel, unless you want to, just drag and drop items to rearrange priorities.&amp;#160; In fact the next version of TFS recognizes that most teams are now using agile approaches and explicitly targets features that will make supporting these processes easier.&lt;/p&gt;  &lt;p&gt;Visual Studio will support test frameworks other than MSTest.&amp;#160; Yes, you read that right. It means means the all new VS test runner will now handle nUnit and XUnit, including code coverage, and provides extension points other test frameworks can use to plug themselves into visual studio.&lt;/p&gt;  &lt;p&gt;Visual Studio will also sport a Crucible/CodeCollab style code review tool, enabling inline commenting and tracking of code review notes, without having to switch out to another tool.&amp;#160; For those teams that use code reviews as part of their process having this directly built into Visual Studio is fantastic news.&lt;/p&gt;  &lt;p&gt;Further to this, Visual Studio will also be able to detect Code Clones.&amp;#160; Microsoft providing tools to help make your code DRY is only going to help improve the overall state of .NET development and I for one think this is a great addition.&lt;/p&gt;  &lt;p&gt;Team Explorer is becoming more task and context oriented, so instead of it being a simple tree view of various nodes with a data focused UI, it will now be more aligned with developer work practices and with the usability improvements should make the learning of the tool much simpler and remove the “I hate doing administrivia” feeling you can sometimes get when keeping work items up to date.&lt;/p&gt;  &lt;p&gt;All in all, these are great improvements in the product and very, very welcome additions.&lt;/p&gt;  &lt;p&gt;Of course, there will be those of you who say that you can already do all or most of this with other tools, and you can, but the integration is often problematic and the benefits of these tools are lost in islands of data.&amp;#160; TFS brings all these pieces together in one place to make things visible across the whole team, provide a view as to what’s happening in the project overall end to end and help improve the collaboration in the software development effort.&amp;#160; Further, many .NET development teams are sadly stymied by management that dictates an “only from Microsoft” attitude, so having these in the base tooling removes some of the impediments that prevent teams from collaborating and communicating well, and hopefully we’ll see the overall state of .NET development improve as a result.&lt;/p&gt;  &lt;p&gt;And in case you were wondering nothing was announced around source control, not that I’m aware of anyway.&amp;#160; If you wish, you can go back to a &lt;a href="http://blogs.msdn.com/b/bharry/archive/2010/01/27/codeplex-now-supports-mercurial.aspx"&gt;post&lt;/a&gt; in Jan 2010 by Brian Harry and read into that what you will.&lt;/p&gt;  &lt;p&gt;For now, go read &lt;a href="http://blogs.msdn.com/b/jasonz/archive/2011/05/16/announcing-alm-roadmap-in-visual-studio-vnext-at-teched.aspx"&gt;Jason’s post&lt;/a&gt;, have a peek at &lt;a href="http://go.microsoft.com/?linkid=9772730"&gt;the whitepaper&lt;/a&gt; to see what else is coming.&amp;#160; Bring on the beta!&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-3097634656806340966?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/3097634656806340966/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2011/05/whats-coming-with-tfs-vnext.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/3097634656806340966'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/3097634656806340966'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2011/05/whats-coming-with-tfs-vnext.html' title='What’s Coming with TFS vNext'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-2988598106871974275</id><published>2011-05-05T21:25:00.001+10:00</published><updated>2011-05-05T21:25:57.319+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='communication'/><category scheme='http://www.blogger.com/atom/ns#' term='readify'/><title type='text'>This is How Support Messages Should Be Done!</title><content type='html'>&lt;p&gt;Taken verbatim from an internal &lt;a href="http://www.readify.net"&gt;Readify&lt;/a&gt; announcement:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;Some weeks ago, a company in Redmond, California released a major update to their current generation of Windows called &amp;quot;SP1&amp;quot;. That's ​a boring name for a product that essentially improves and cleans up windows, so we'll refer to this product as &amp;quot;Windex&amp;quot; from now on. Windex is a great thing. IT took the time to put it on our own computers and tried it out. Sure enough, all our windows are cleaner and just better. There have been no bad effects to using Windex as yet.&lt;/p&gt;    &lt;p&gt;Emboldened, we set upon a course of putting Windex everywhere we could. We gave it to our friends. We gave it to our colleagues. Everyone loves Windex! This week, we've been busily applying Windex to all the Readify servers. It's been a very positive experience and all Readify's servers have taken to Windex without complaint. In fact, they love Windex!&lt;/p&gt;    &lt;p&gt;All our servers for the most part however all rely on three very powerful master machines that are the unsung heroes of our network. These magnificent machines are the quiet achievers of the Cloud network - they manage, control and look after all the servers you know and love; as well as all the servers you don't know and love yet (be assured you'd love them if you knew them). We'll call these three servers the &amp;quot;Hosts&amp;quot; of the network. These three hosts don't yet have Windex. They've seen Windex be put onto all the servers they look after, and have been patiently waiting for someone to come and apply some Windex to them.&lt;/p&gt;    &lt;p&gt;That time has come!&lt;/p&gt;    &lt;p&gt;... but there's a catch. As a part of applying Windex to any machine, you need to close and reopen the windows on it. I'll call this process &amp;quot;Rebooting&amp;quot;. The rest of the network can't do anything if the hosts that look after them are rebooting. So, while the network isn't doing anything, neither will any of our staff. As a result, putting Windex on the hosts during the week would be bad™.&lt;/p&gt;    &lt;p&gt;So, starting from about 2am on Sunday morning, while everyone is either in bed or out partying and not caring; These quiet achievers of the Readify family will finally get their Windex. If all goes well, they will be back up and running (and much better and cleaner) by the time you start trying using the network on Sunday. If all doesn’t go well, it’s ok we can still email you and tell you it didn’t go well – and trust us: We’ll know about it before you do. We’ll have all day Sunday to fix any snafu that may happen and still have the whole network ready to run by Monday morning. You may need to consider not doing any work on Sunday.&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;Kudos to the fantastic Nathan Thomas for always keeping us in the loop as to what’s happening.&amp;#160; I love it!&lt;/p&gt;  &lt;p&gt;What’s the best downtime message you’ve ever seen?&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-2988598106871974275?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/2988598106871974275/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2011/05/this-is-how-support-messages-should-be.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/2988598106871974275'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/2988598106871974275'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2011/05/this-is-how-support-messages-should-be.html' title='This is How Support Messages Should Be Done!'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-7553739518097190805</id><published>2011-05-04T09:51:00.001+10:00</published><updated>2011-05-04T09:51:01.189+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='usability'/><category scheme='http://www.blogger.com/atom/ns#' term='general'/><title type='text'>Windows Media Player and “In Use” External USB Hard Drives</title><content type='html'>&lt;p&gt;I have a lovely new 1TB external hard drive and moved all my music onto it a short while ago.&amp;#160; Now even though I have iPods and iPhones, for playing that music from my laptop, ITunes on Windows is pretty poor so I’ve tended to use Media Player and, more recently, Zune for that task.&lt;/p&gt;  &lt;p&gt;Now, as you would expect I wanted my music into these programs automatically I included the music folder in my library as shown:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_5dD_rBQSs2o/TcCT2jPv_9I/AAAAAAAABDk/Qr8wdcv0euY/s1600-h/image%5B40%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_5dD_rBQSs2o/TcCUAbr9zLI/AAAAAAAABDo/2H96VJGaBh8/image_thumb%5B14%5D.png?imgmax=800" width="484" height="439" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Now given I’m on a laptop and I usually want to leave work at the end of the day I’ll Safely Remove the drive from my laptop before packing up, but now when I do I get either the infamous &lt;em&gt;“Windows can't stop your 'Generic Volume' device because it is in use”&lt;/em&gt; message or the “Problem Ejecting USB Mass Storage Device” message as shown:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_5dD_rBQSs2o/TcCUDGmw4lI/AAAAAAAABDs/2bGLl0Tytq4/s1600-h/image%5B41%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_5dD_rBQSs2o/TcCUFaIcs9I/AAAAAAAABDw/BrSMXMeeZ94/image_thumb%5B15%5D.png?imgmax=800" width="479" height="175" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;But I closed Media Player (and everything else) so it can’t be locking files can it? &lt;/p&gt;  &lt;p&gt;Now, as it happens, in the past I’ve seen Windows Search Indexing locking external drives, but I’d already made sure that the drive isn’t indexed by turning off the flag in the properties window:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_5dD_rBQSs2o/TcCUHyNNaTI/AAAAAAAABD0/MseDw8Wae00/s1600-h/image%5B43%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_5dD_rBQSs2o/TcCUKFFMj4I/AAAAAAAABD4/D10qSrSC0M4/image_thumb%5B17%5D.png?imgmax=800" width="381" height="509" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;and if I look at the Indexing Options in Control Panel I see this:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_5dD_rBQSs2o/TcCUNiGO52I/AAAAAAAABD8/mYTzDdiopmU/s1600-h/image%5B45%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_5dD_rBQSs2o/TcCUQkXq-QI/AAAAAAAABEA/FOTLjPDuMKw/image_thumb%5B19%5D.png?imgmax=800" width="418" height="585" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;You can see that the G: drive (the external) isn’t indexed, yet if you look at the location summary in the bottom pane there’s a folder called Music there.&amp;#160; What gives?&lt;/p&gt;  &lt;p&gt;So I expand the G: drive settings and what do I see? That the folder is, in fact, indexed by Windows Search and it is very likely locking the drive.&amp;#160; What the…?! Why isn’t the G:\ drive showing with a greyed checkbox to indicate that one or more child folders are included.&amp;#160; Thanks for the great UI there, Microsoft.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_5dD_rBQSs2o/TcCUUMeLckI/AAAAAAAABEE/1HpLqLKIguU/s1600-h/image%5B47%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_5dD_rBQSs2o/TcCUdoDCO5I/AAAAAAAABEI/rLCen816xms/image_thumb%5B21%5D.png?imgmax=800" width="418" height="585" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;So if I just clear it now, then I’ll be able to safely remove the drive, but this is what happens in Media Player’s settings:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_5dD_rBQSs2o/TcCUhE2sQhI/AAAAAAAABEM/zKsCq3yqwss/s1600-h/image%5B48%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_5dD_rBQSs2o/TcCUjiXb0XI/AAAAAAAABEQ/Vnc3rYxMprM/image_thumb%5B22%5D.png?imgmax=800" width="536" height="486" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Which of means that new items I add to my Music folder won’t automatically appear.&amp;#160; That’s not what I want.&amp;#160; In fact it sucks.&lt;/p&gt;  &lt;p&gt;Maybe I should just use the Zune player instead? Unfortunately, by default, Zune relies on the Windows Media Player library settings, so to break the dependency I have to go into Zune settings and unlink the Zune folders from the windows libraries.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_5dD_rBQSs2o/TcCUk3uWbjI/AAAAAAAABEU/X3rdTWZoDTk/s1600-h/image%5B49%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_5dD_rBQSs2o/TcCUmGgz-5I/AAAAAAAABEY/NAjPsSNsEeM/image_thumb%5B23%5D.png?imgmax=800" width="390" height="164" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Now I can add the Music folder from the external USB drive and Zune will automatically index the folder and pick up changes to the contents, just as I wanted.&amp;#160; And it does it without keeping the drive locked when I exit the program.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_5dD_rBQSs2o/TcCUslWaVqI/AAAAAAAABEc/xrSWJpVac6M/s1600-h/image%5B50%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_5dD_rBQSs2o/TcCUzF4jYaI/AAAAAAAABEg/mmLwQWR3l5E/image_thumb%5B24%5D.png?imgmax=800" width="380" height="99" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;So now when I want to go home, I can just close Zune and then safely remove the drive as I would expect.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_5dD_rBQSs2o/TcCU0jwAZlI/AAAAAAAABEk/Gn5HvIKx-9Y/s1600-h/image%5B51%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_5dD_rBQSs2o/TcCU3_-3iWI/AAAAAAAABEo/oaVUclTbVcg/image_thumb%5B25%5D.png?imgmax=800" width="360" height="133" /&gt;&lt;/a&gt;&lt;/p&gt;                                &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;Phew! Talk about a major pain in the butt! How is a normal, non technical person ever going to figure this out for themselves.&amp;#160; Maybe I should’ve just stuck with iTunes – at least it doesn’t lock your external drives on you.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-7553739518097190805?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/7553739518097190805/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2011/05/windows-media-player-and-in-use.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/7553739518097190805'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/7553739518097190805'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2011/05/windows-media-player-and-in-use.html' title='Windows Media Player and “In Use” External USB Hard Drives'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/_5dD_rBQSs2o/TcCUAbr9zLI/AAAAAAAABDo/2H96VJGaBh8/s72-c/image_thumb%5B14%5D.png?imgmax=800' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-649449717840971075</id><published>2011-05-01T14:01:00.001+10:00</published><updated>2011-05-01T14:01:04.303+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='agile'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><title type='text'>Oh Dear</title><content type='html'>&lt;p&gt;Found via &lt;a href="http://blog.wekeroad.com/"&gt;Rob Conery’s blog&lt;/a&gt; – &lt;a href="http://www.zdnet.com/news/better-website-development-and-deployment-a-practical-methodology/6222035?tag=content;search-results-rivers"&gt;A Methodology for Better Website Development&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;My eyes! They burn!! Make it stop!!!&lt;/p&gt;  &lt;p&gt;It’s waterfall being touted as the best way to do web development! Seriously!? Who wrote this article? Wait a second while I look….&amp;#160; Oh, it’s the CEO of Ektron, the makers of Ektron CMS. A product so good that the only times I’ve heard of it has been from companies that bought it, got screwed by poor service, bad documentation, horrible support and the vendor’s inability to supply updates in a timely manner, and that have then written it off as a mistake, and then kicked it out so it can be replaced by other commercial products or solutions from that most waterfall-ish of development environments, the open source world!&amp;#160; This explains so much! &lt;/p&gt;  &lt;p&gt;It seriously annoys me that people write this sort of article as if it’s the one true way and even more when a publisher like ZDNet doesn’t include a public health warning with it.&lt;/p&gt;  &lt;p&gt;This isn’t to say agile is the only way to do web development either, not by a long shot.&amp;#160; Agile is a mechanism that has been shown to work well in web development and we’ve seen time and again that short development cycles and rapid, continuous releases are a path to success.&amp;#160; The real secret to success though? Find the best people you can, educate them in how to best communicate with you and each other, then tell them what you want and then get the hell out of their way!&lt;/p&gt;  &lt;p&gt;Individuals and interactions over process and tools.&lt;/p&gt;  &lt;p&gt;I think I need to take an aspirin and go have a lie down. Reading that article gave me a small aneurism.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-649449717840971075?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/649449717840971075/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2011/05/oh-dear.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/649449717840971075'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/649449717840971075'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2011/05/oh-dear.html' title='Oh Dear'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-2925498411686415823</id><published>2011-04-06T07:37:00.001+10:00</published><updated>2011-04-06T07:37:56.593+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='general'/><title type='text'>R.I.P. Dear Desktop</title><content type='html'>&lt;p&gt;Back at the start of 2009 I picked up an Alienware desktop (&lt;a href="http://www.richard-banks.org/2009/02/my-new-desktop.html"&gt;post and pictures here&lt;/a&gt;) and at the time it was perfect for my needs, and how I did love it so, but as theses stories go time passed and I found myself neglecting my desktop and spending more and more time with my other love – my laptop.&amp;#160; It’s what I use for my day to day work needs and as fate would have it where I ended up running almost everything that I once used my desktop for including my PC gaming since it has a decent enough graphics card and I get to take it with me when I travel.&amp;#160; The desktop has slowly been turning into nothing more than the place where I host my iTunes music collection.&amp;#160; Sad, I know.&lt;/p&gt;  &lt;p&gt;Now recently this machine has been randomly shutting down. Initially I thought it was related to a video card driver update that had just came through shortly after Win 7 SP1, but it was a little too random and a driver rollback didn’t fix anything so I cracked open the case to check the internals, and what do I find? That the heat sink and cooling fan are loose in their mountings!&amp;#160; The plastic bracket used to hold the heat sink and CPU together had cracked and broken meaning that the CPU had only minimal contact with the heat sink.&amp;#160; This would explain the random shutdowns.&lt;/p&gt;  &lt;p&gt;So now I have a decision.&amp;#160; Do I replace a few parts and get it running again or bid farewell to the desktop, and treat the laptop as the genuine desktop replacement it is?&amp;#160; I’ve decided it’s time to say R.I.P. dear desktop and time to spend all my time with just the laptop.&amp;#160; I’ll get an external HDD enclosure so I can still get my music and other files off the desktop drive, but that will be all.&lt;/p&gt;  &lt;p&gt;So a question for you.&amp;#160; Would you do the same if it was you, have you long said goodbye to your desktop and think I’ve been a luddite for hanging on to mine for so long, or would keep the big box and use simply upgrade the motherboard and CPU, etc?&amp;#160; I’m curious.&lt;/p&gt;  &lt;p&gt;P.S. If anyone wants to make an offer on a second hand Alienware desktop, needing a new CPU &amp;amp; motherboard with 4GB RAM and a decent graphics card, then let me know either via email or twitter.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-2925498411686415823?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/2925498411686415823/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2011/04/rip-dear-desktop.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/2925498411686415823'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/2925498411686415823'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2011/04/rip-dear-desktop.html' title='R.I.P. Dear Desktop'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-7906839850292106361</id><published>2011-04-05T15:34:00.000+10:00</published><updated>2011-04-05T15:34:03.600+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='silverlight'/><title type='text'>WinForms and Silverlight Integration With a Twist</title><content type='html'>&lt;p&gt;I’m doing some work with a customer at the moment that has a large and complex application written in Progress, and they are wanting to do some work to add new features to it, but want to do so via Silverlight.&lt;/p&gt;  &lt;p&gt;Now the way this application works from a UI perspective is that there is an application host shell with a menu and so forth, and when people request a screen it creates a new tab and hosts the appropriate screen in that tab.&amp;#160; That screen could be a Progress GUI screen, a console screen or a web page, and now we need to add Silverlight screens to the mix as well.&lt;/p&gt;  &lt;p&gt;Something like this:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_5dD_rBQSs2o/TZqn8f3EntI/AAAAAAAABCw/ockzMvx7aQs/s1600-h/image3.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_5dD_rBQSs2o/TZqn9bgQijI/AAAAAAAABC0/pdEMYb0lRrA/image_thumb1.png?imgmax=800" width="484" height="156" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Now for this to work we have to jump through a few hoops.&amp;#160; We need Silverlight to be hosted in a web page, which means we’ll need to host a web browser control somewhere.&amp;#160; We’ll also need to host that browser control in a WinForms user control since in Progress we can add a wrapper around the user control to make the whole thing callable via the Progress shell, as well as being able to receive events.&amp;#160; When it’s put together it looks something like this:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_5dD_rBQSs2o/TZqn9px9SBI/AAAAAAAABC4/-EnnCCstiyk/s1600-h/image8.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_5dD_rBQSs2o/TZqn-fbZfuI/AAAAAAAABC8/1uESkElc-cg/image_thumb4.png?imgmax=800" width="231" height="244" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;What we’re also going to do is make sure we have just one Silverlight application containing all the Silverlight forms we want.&amp;#160; We don’t want to create an application per form since that would likely turn into a huge maintenance and deployment problem and make development overly complex.&lt;/p&gt;  &lt;p&gt;Now for all of this to work we will use the Silverlight to HTML bridge as well as the ability to have pages in the WebBrowser control talk to a .NET object via COM.&lt;/p&gt;  &lt;h2&gt;Wrap the WebBrowser Control&lt;/h2&gt;  &lt;p&gt;Let’s start by wrapping the web browser control.&amp;#160; We could simply subclass it to do what we need, but if we do we won’t be able to get information back from the Silverlight UI the way we want, so we’ll instead create a custom user control and host the WebBrowser in that.&lt;/p&gt;  &lt;p&gt;We’ll also create a few methods on the control to navigate to the page where the Silverlight application is hosted, and to request the particular Silverlight form we want.&lt;/p&gt;  &lt;p&gt;Now this code isn’t quite so straightforward since we have some timing issues to deal with.&amp;#160; If the Progress code simply calls “Navigate” and then “Open Form” I could be requesting information from the Silverlight application before either the web page or the Silverlight app has finished loading and if that happens then we’ll have errors thrown in our UI.&amp;#160; This isn’t so good.&amp;#160; We need to wait until things are ready before commencing.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_5dD_rBQSs2o/TZqn-y1hXiI/AAAAAAAABDA/d0KvZoKeI6c/s1600-h/image11.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_5dD_rBQSs2o/TZqn_8b2Q1I/AAAAAAAABDE/zhA-NWK6IPY/image_thumb5.png?imgmax=800" width="200" height="244" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Here’s the code we need.&amp;#160; Note that a variable number of arguments for the Silverlight pages is catered for by passing an Object[] array as a parameter.&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:46d4597d-1d6d-4eca-b7ed-e0066862f311" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: c#;"&gt;public partial class SilverlightHost : UserControl&lt;br /&gt;{&lt;br /&gt; public SilverlightHost()&lt;br /&gt; {&lt;br /&gt;  InitializeComponent();&lt;br /&gt;  webBrowser1.AllowNavigation = false;&lt;br /&gt;  webBrowser1.AllowWebBrowserDrop = false;&lt;br /&gt;  webBrowser1.IsWebBrowserContextMenuEnabled = false;&lt;br /&gt;  webBrowser1.WebBrowserShortcutsEnabled = false;&lt;br /&gt;  webBrowser1.DocumentCompleted += (s, e) =&amp;gt; documentLoaded = true;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public void LoadSilverlightFrom(string url)&lt;br /&gt; {&lt;br /&gt;  NavigateToUrl(url);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; private bool documentLoaded = false;&lt;br /&gt;&lt;br /&gt; private void NavigateToUrl(string url)&lt;br /&gt; {&lt;br /&gt;  documentLoaded = false;&lt;br /&gt;  webBrowser1.Navigate(url);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public void ShowForm(string formName, object[] parameters)&lt;br /&gt; {&lt;br /&gt;  while (!documentLoaded)&lt;br /&gt;   Application.DoEvents();&lt;br /&gt;  var args = new List&amp;lt;object&amp;gt; {formName};&lt;br /&gt;  if (parameters != null)&lt;br /&gt;   args.AddRange(parameters);&lt;br /&gt;  webBrowser1.Document.InvokeScript("ShowForm", args.ToArray());&lt;br /&gt; }&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;h2&gt;Edit the Web Page For Silverlight Interaction&lt;/h2&gt;&lt;p&gt;The keen of sight will have noticed that in the ShowForm method above we call a JavaScript script on the web page called ShowForm.&amp;#160; This is not a standard script, we need to go and add it.&lt;/p&gt;&lt;p&gt;We’ll also need to do a little work in our JavaScript to handle the fact that the script can be called before the Silverlight object has actually completed initialisation.&lt;/p&gt;&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:b5edde9a-4861-4ec9-b903-439d241fdc7f" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: javascript; highlight: [9];"&gt;&amp;lt;body&amp;gt;&lt;br /&gt;    &amp;lt;form id="form1" runat="server" style="height:100%"&amp;gt;&lt;br /&gt;    &amp;lt;div id="silverlightControlHost"&amp;gt;&lt;br /&gt;        &amp;lt;object data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="100%" height="100%" id="silverlightControl"&amp;gt;&lt;br /&gt;   &amp;lt;param name="source" value="ClientBin/HostedSilverlightPOC.xap"/&amp;gt;&lt;br /&gt;   &amp;lt;param name="onError" value="onSilverlightError" /&amp;gt;&lt;br /&gt;   &amp;lt;param name="background" value="white" /&amp;gt;&lt;br /&gt;   &amp;lt;param name="minRuntimeVersion" value="4.0.50826.0" /&amp;gt;&lt;br /&gt;   &amp;lt;param name="onload" value="SilverlightLoaded" /&amp;gt;&lt;br /&gt;   &amp;lt;param name="autoUpgrade" value="true" /&amp;gt;&lt;br /&gt;   &amp;lt;a href="http://go.microsoft.com/fwlink/?LinkID=149156&amp;amp;v=4.0.50826.0" style="text-decoration:none"&amp;gt;&lt;br /&gt;   &amp;lt;img src="http://go.microsoft.com/fwlink/?LinkId=161376" alt="Get Microsoft Silverlight" style="border-style:none"/&amp;gt;&lt;br /&gt;   &amp;lt;/a&amp;gt;&lt;br /&gt;    &amp;lt;/object&amp;gt;&amp;lt;iframe id="_sl_historyFrame" style="visibility:hidden;height:0px;width:0px;border:0px"&amp;gt;&amp;lt;/iframe&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/form&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;script type="text/javascript"&amp;gt;&lt;br /&gt;        var silverlightReady = false;&lt;br /&gt;        var navigateToFunction;&lt;br /&gt;        var formName;&lt;br /&gt;        var args;&lt;br /&gt;&lt;br /&gt;        function SilverlightLoaded(sender, args) {&lt;br /&gt;            silverlightReady = true;&lt;br /&gt;            if (typeof(navigateToFunction) == 'function')&lt;br /&gt;                navigateToFunction();&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        function ShowForm() {&lt;br /&gt;            args = Array.prototype.slice.call(arguments);  &lt;br /&gt;            formName = args.shift();&lt;br /&gt;            var control = document.getElementById('silverlightControl');&lt;br /&gt;            if (silverlightReady)&lt;br /&gt;                control.Content.Page.NavigateTo(formName, args);&lt;br /&gt;            else&lt;br /&gt;                navigateToFunction = function () {&lt;br /&gt;                    control.Content.Page.NavigateTo(formName, args);&lt;br /&gt;                };&lt;br /&gt;        }&lt;br /&gt;    &amp;lt;/script&amp;gt;&lt;br /&gt;&amp;lt;/body&amp;gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So a few things to note. First we hook the Silverlight onLoad event (highlighted) and use it to set a Boolean indicating wether the control has finished loading or not.&amp;#160; Once the loading is complete the SilverlightLoaded function is processed where it toggles the flag and calls the navigateToFunction, if it’s been populated.&lt;/p&gt;&lt;p&gt;In the ShowForm function we check if the Silverlight control has loaded and if it has we call the NavigateTo method (we’ll get to that in a minute) and if not we populate the navigateToFunction which will be called once Silverlight has finished loading.&lt;/p&gt;&lt;p&gt;If you’re wondering the JavaScript call will mash the form name and the Object array for the parameters into a single arguments variable.&amp;#160; We strip the form&lt;/p&gt;&lt;p&gt;And yes, for those wondering, this code can be made DRYer and cleaner. I left it like this to (hopefully) aid understanding.&lt;/p&gt;&lt;h2&gt;The HTML to Silverlight Bridge&lt;/h2&gt;&lt;p&gt;So now we have our WinForms control calling the JavaScript on our web page, but we now need to do a few things in Silverlight to receive that call.&amp;#160; This is pretty simple:&lt;/p&gt;&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:d35ecdad-cce3-4d4a-88cd-9ce03fe3b8b9" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: c#;"&gt;public partial class NavigationControl : UserControl&lt;br /&gt;{&lt;br /&gt; public NavigationControl()&lt;br /&gt; {&lt;br /&gt;  InitializeComponent();&lt;br /&gt;  HtmlPage.RegisterScriptableObject("Page", this);&lt;br /&gt;&lt;br /&gt;  pageLaunchers.Add("Main", (p) =&amp;gt;&lt;br /&gt;  {&lt;br /&gt;   var mp = new MainPage();&lt;br /&gt;   mp.textBox1.Text = (string)p[0];&lt;br /&gt;   return mp;&lt;br /&gt;  });&lt;br /&gt;  pageLaunchers.Add("AnotherOne", p =&amp;gt;&lt;br /&gt;  {&lt;br /&gt;   var page = new AnotherOne();&lt;br /&gt;   return page;&lt;br /&gt;  });&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; private readonly Dictionary&amp;lt;string, Func&amp;lt;IList&amp;lt;object&amp;gt;, UserControl&amp;gt;&amp;gt; pageLaunchers = new Dictionary&amp;lt;string, Func&amp;lt;IList&amp;lt;object&amp;gt;, UserControl&amp;gt;&amp;gt;();&lt;br /&gt;&lt;br /&gt; [ScriptableMember]&lt;br /&gt; public void NavigateTo(string controlName, ScriptObject parameterList)&lt;br /&gt; {&lt;br /&gt;  var paramList = parameterList.ConvertTo&amp;lt;List&amp;lt;object&amp;gt;&amp;gt;();&lt;br /&gt;  var launcher = pageLaunchers[controlName];&lt;br /&gt;  LayoutRoot.Children.Clear();&lt;br /&gt;  var form = launcher(paramList);&lt;br /&gt;  LayoutRoot.Children.Add(form);&lt;br /&gt; }&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The Navigation control here is an empty user control.&amp;#160; There is nothing in it by default so we can load other controls up into it as the host.&amp;#160; These other controls are the pages that we’re wanting our Progress code to be able to call.&lt;/p&gt;&lt;p&gt;Now to get the Silverlight bridge working we simply register the user control as a scriptable object and then mark the methods we want available to JavaScript with the [ScriptableMember] attribute.&amp;#160; What you’ll note is that the Object[] array from JavaScript comes through to our Silverlight method as a ScriptObject.&amp;#160; We need to convert it in Silverlight to an object collection before we can do things with it.&lt;/p&gt;&lt;p&gt;You’ll also notice that we’re using a collection of initialiser methods to avoid huge case or if statements that map from the string form name we pass through from the WinForms control to the actual form we’re wishing to display.&lt;/p&gt;&lt;p&gt;When we receive a request to load a form we check the collection for an initialiser, clear the current page and initialise the new one.&amp;#160; Once we have a reference to it, we load the form up as a child of the navigation control which will automatically show it to our end users, and we’re done.&lt;/p&gt;&lt;h2&gt;Raising Events Back In WinForms&lt;/h2&gt;&lt;p&gt;So we can now display any form we want to from Progress.&amp;#160; We then want to be able to click a button in Silverlight and have an event with some attached data raised back in WinForms for Progress to listen to.&amp;#160; Let’s create an event indicating that the Silverlight app is asking the host app to open another form, such as a console or Progress GUI form.&lt;/p&gt;&lt;p&gt;In Silverlight we can create a simple method as follows:&lt;/p&gt;&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:237a6af6-b3db-4d1a-966b-1fd9c6b8283c" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: c#;"&gt;public static void AskHostForForm(string formName, string p1 = "", string p2 = "", string p3 = "")&lt;br /&gt;{&lt;br /&gt; var form = (ScriptObject)HtmlPage.Window.GetProperty("external");&lt;br /&gt; form.Invoke("AskHostToOpenForm", formName, p1, p2, p3);&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And call this from a button click event.&amp;#160; For ease of use by all forms this has been made static and resides in the NavigationControl class.&lt;/p&gt;&lt;p&gt;The HtmlPage.Window.GetProperty(“external”) call will retrieve from the web page hosting the Silverlight control any external reference objects on it.&amp;#160; In our case that’s going to be the WinForms control.&amp;#160; And we’re going to need to ensure that we create an AskHostToOpenForm method&lt;/p&gt;&lt;p&gt;Back in our WinForms control we need to now expose our control as a ComVisible component and tell the WebBroswer control that it is an ObjectForScripting.&amp;#160; Doing so will automatically make all public methods on the control available for calling either from JavaScript or from Silverlight via the HTML Bridge as shown in the code snippet above.&lt;/p&gt;&lt;p&gt;We also need to add some code to our WinForms custom control to raise an event when the method is called (so that Progress can do something with it)&lt;/p&gt;&lt;p&gt;Here’s the code.&lt;/p&gt;&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:8ece94bd-36ce-48c6-b08a-48497b23667b" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: c#;"&gt;[ComVisible(true)]&lt;br /&gt;public partial class SilverlightHost : UserControl&lt;br /&gt;{&lt;br /&gt; public event OpenFormRequestedHandler OpenFormRequested;&lt;br /&gt;&lt;br /&gt; public SilverlightHost()&lt;br /&gt; {&lt;br /&gt;  InitializeComponent();&lt;br /&gt;  ...&lt;br /&gt;  webBrowser1.ObjectForScripting = this;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; ...&lt;br /&gt;&lt;br /&gt; public void AskHostToOpenForm(string formName, string p1, string p2, string p3)&lt;br /&gt; {&lt;br /&gt;  var args = new OpenFormRequestedArgs(formName, new object[] {p1, p2, p3});&lt;br /&gt;  if (OpenFormRequested != null)&lt;br /&gt;  {&lt;br /&gt;   OpenFormRequested(this, args);&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public delegate void OpenFormRequestedHandler(object sender, OpenFormRequestedArgs args);&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You’ll see that the number of parameters is fixed.&amp;#160; I tried messing about with a variable number of arguments but when I did so the parameters were simply COM objects and there’s no way I was going to mess around with COM :-)&amp;#160; I’m not that crazy!&lt;/p&gt;&lt;h2&gt;Wrap Up&lt;/h2&gt;&lt;p&gt;So there we have it, we can now have a Progress application (or any other WinForms app) that hosts a Silverlight form within a tab of the application and be able to get events back from that form when required.&lt;/p&gt;&lt;p&gt;&lt;a href="https://github.com/rbanks54/RichardsSamples/tree/master/HostedSilverlightPOC"&gt;The code for this is up on GitHub&lt;/a&gt;, so feel free to grab it and have a look at how it all fits together.&amp;#160; If you’ve got any improvements to it that you want to suggest then please let me know.&amp;#160; I’m always happy to get feedback!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-7906839850292106361?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/7906839850292106361/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2011/04/winforms-and-silverlight-integration.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/7906839850292106361'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/7906839850292106361'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2011/04/winforms-and-silverlight-integration.html' title='WinForms and Silverlight Integration With a Twist'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_5dD_rBQSs2o/TZqn9bgQijI/AAAAAAAABC0/pdEMYb0lRrA/s72-c/image_thumb1.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-562769529097358519</id><published>2011-03-30T10:27:00.001+11:00</published><updated>2011-03-30T10:27:31.653+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='general'/><title type='text'>Bravo, Suncorp! Bravo!</title><content type='html'>&lt;p&gt;&lt;a href="http://www.theaustralian.com.au/australian-it/suncorp-goes-byo-in-hardware-as-staff-are-encouraged-to-plug-in-their-devices/story-e6frgakx-1226029655986"&gt;BYO Hardware at SunCorp&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;To quote:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;“In what could prove to be a watershed moment for corporate Australia, financial services firm Suncorp will let thousands of employees use their personal computers, including Apple iPads, at work.&amp;quot;&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;In case it’s not obvious, the approach taken means that the devices people choose to use will just be thin clients accessing Citrix virtualised applications and web based internal apps and I think it’s fair to assume that people will need to ensure anti-virus and firewalls are running on their devices where appropriate.&lt;/p&gt;  &lt;p&gt;I for one think this is a great move and I hope more large enterprises follow suit.&amp;#160; The more places I can use my own hardware and the fewer places where I have to use horrible desktops, with awful keyboards and crappy screens the better.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-562769529097358519?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/562769529097358519/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2011/03/bravo-suncorp-bravo.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/562769529097358519'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/562769529097358519'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2011/03/bravo-suncorp-bravo.html' title='Bravo, Suncorp! Bravo!'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-5977254732777513937</id><published>2011-03-28T12:39:00.001+11:00</published><updated>2011-03-28T12:39:29.697+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='TFS'/><title type='text'>How To: In Place Branch Switching with TFS 2010</title><content type='html'>&lt;p&gt;One of the nice features of some of the newer version control systems is the ability to do fast in-place branch switching.&lt;/p&gt;  &lt;p&gt;This is really useful when you are working on a web application and you have mapped an IIS virtual directory to your source folder and want to switch between branches without having to reconfigure your IIS mappings.&lt;/p&gt;  &lt;p&gt;If you’re using TFS then most likely branch switching is a pain because you have branches mapped to separate folders and have to constantly close the solution, remap IIS and open the solution again from the other branch folder.&amp;#160; But you don’t have to! You can actually have multiple branches using the same local working folder, just not at the same time.&lt;/p&gt;  &lt;p&gt;Here’s how to do in place branch switching with TFS 2010.&lt;/p&gt;  &lt;h2&gt;Starting Point&lt;/h2&gt;  &lt;p&gt;Let’s assume we have the following source tree in TFS, with a &lt;a href="http://www.richard-banks.org/2011/02/branch-per-story-pattern-and-tfs.html"&gt;branch per story&lt;/a&gt; arrangement (this is using Source Control Explorer in Visual Studio 2010).&amp;#160; We currently have no branches mapped, thus the grey folders.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_5dD_rBQSs2o/TY_mUO9cNkI/AAAAAAAABAA/aJptIeTLV3M/s1600-h/image%5B76%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_5dD_rBQSs2o/TY_mU_zIehI/AAAAAAAABAE/f02wdkx1Jc8/image_thumb%5B26%5D.png?imgmax=800" width="230" height="183" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Let’s now map the integration branch and then get the source locally to our working folder.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_5dD_rBQSs2o/TY_mVZRkWWI/AAAAAAAABAI/mbncmc4Mu1s/s1600-h/image%5B80%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_5dD_rBQSs2o/TY_mWE1vY8I/AAAAAAAABAM/JujiVRuyB0E/image_thumb%5B30%5D.png?imgmax=800" width="480" height="146" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_5dD_rBQSs2o/TY_mW-jncfI/AAAAAAAABAQ/IWj461fsliU/s1600-h/image%5B81%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_5dD_rBQSs2o/TY_mXtTmanI/AAAAAAAABAU/pOzBGzwUxeg/image_thumb%5B31%5D.png?imgmax=800" width="487" height="199" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;When we look at the workspace details we then see this:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_5dD_rBQSs2o/TY_mYJBBqOI/AAAAAAAABAY/WW5BcVgAVok/s1600-h/image%5B83%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_5dD_rBQSs2o/TY_mZKn_XSI/AAAAAAAABAc/0dTvUneGFKY/image_thumb%5B33%5D.png?imgmax=800" width="480" height="390" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;h2&gt;Switching Branches In Place&lt;/h2&gt;  &lt;p&gt;Now let’s say I want to switch to one of my story branches to make some changes.&amp;#160; The easiest way is right click the folder in source control and select “Map to Local Folder”&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_5dD_rBQSs2o/TY_mZ1yexNI/AAAAAAAABAg/IEyjKkKtQBk/s1600-h/image%5B95%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_5dD_rBQSs2o/TY_masPIH1I/AAAAAAAABAk/Le0MIVzqgxo/image_thumb%5B41%5D.png?imgmax=800" width="118" height="240" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Alternatively, if you have the power tools installed into VS2010, you could click on the path mapping at the top of the source control explorer window:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_5dD_rBQSs2o/TY_mbUJxMYI/AAAAAAAABAo/QDl2ABRA1t4/s1600-h/image%5B102%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_5dD_rBQSs2o/TY_mcVpAHFI/AAAAAAAABAs/pJ6H8MkyKtk/image_thumb%5B48%5D.png?imgmax=800" width="484" height="274" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;We then enter the same working folder that we used for the integration branch above.&amp;#160; Given we’re wanting to reuse our working folder this should be obvious.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_5dD_rBQSs2o/TY_mdBHOi3I/AAAAAAAABAw/tDKRRKD_dOU/s1600-h/image%5B99%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_5dD_rBQSs2o/TY_mdrctGoI/AAAAAAAABA0/eNYGoNkOggg/image_thumb%5B45%5D.png?imgmax=800" width="480" height="146" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_5dD_rBQSs2o/TY_medVd_eI/AAAAAAAABA4/AKO2R9FAbtQ/s1600-h/image%5B100%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_5dD_rBQSs2o/TY_mfPzoLEI/AAAAAAAABA8/WoJOa1eWYrk/image_thumb%5B46%5D.png?imgmax=800" width="487" height="199" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;If we now look at source control explorer we’ll see that the integration branch is no longer mapped (it’s greyed out) and that our 2703 story is now the active branch.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_5dD_rBQSs2o/TY_mfwC8-1I/AAAAAAAABBA/LkGgbdqvHOM/s1600-h/image%5B104%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_5dD_rBQSs2o/TY_mgZ-6dfI/AAAAAAAABBE/WH1gkjMRc7Q/image_thumb%5B50%5D.png?imgmax=800" width="232" height="202" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;We can confirm this by looking at the workspace details, seeing that the integration branch is no longer mapped and that our story branch is set to the working folder.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_5dD_rBQSs2o/TY_mhLy6FtI/AAAAAAAABBI/Y1W5VdjvvlQ/s1600-h/image%5B107%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_5dD_rBQSs2o/TY_miP__s3I/AAAAAAAABBM/V9hwpif8SdU/image_thumb%5B53%5D.png?imgmax=800" width="480" height="390" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Note that if you change the source control folder in the working folders section of the workspace dialog you will also trigger the remapping operation.&lt;/p&gt;  &lt;p&gt;When the get operation executes to pull down files, TFS will only send you files that are different between the two branches. It’s not a full recursive get, which means this operation is quite fast.&lt;/p&gt;  &lt;p&gt;Note that this only works if we are switching between TFS branches.&amp;#160; If you try switching between non-branch folders you’ll get an error indicating the folder is already mapped.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_5dD_rBQSs2o/TY_mi3sp2FI/AAAAAAAABBQ/Wcxm9bP4oEM/s1600-h/image%5B109%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_5dD_rBQSs2o/TY_mjS2bv_I/AAAAAAAABBU/5cbKL1A3IMM/image_thumb%5B55%5D.png?imgmax=800" width="489" height="199" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;h2&gt;Command Line Goodness&lt;/h2&gt;  &lt;p&gt;OK, so let’s go and switch back to the integration branch but this time we’ll do it via the command line.&amp;#160; We need two commands for this:&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;tf workfold /map &lt;/strong&gt;&amp;lt;&amp;lt;&amp;lt; to change the workspace folder mapping&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;tf get /remap&lt;/strong&gt; &amp;lt;&amp;lt;&amp;lt; to bring the local folder contents up to date&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_5dD_rBQSs2o/TY_mkFubb2I/AAAAAAAABBY/L60QphCXQLk/s1600-h/image%5B110%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_5dD_rBQSs2o/TY_mkpAEa6I/AAAAAAAABBc/KhFwJqpJx_w/image_thumb%5B56%5D.png?imgmax=800" width="532" height="194" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;And as you can see TFS 2010 has only given us the files that are different between the branches.&amp;#160; Nice.&lt;/p&gt;  &lt;p&gt;Now, you may ask, why would I use this method and not do it all via Visual Studio 2010?&lt;/p&gt;  &lt;p&gt;Well, firstly it’s faster to do it this way (at least for me it is), plus it’s very easy to create a small batch file to make this even quicker and simpler, but a more pragmatic reason is that if I have a solution open when branch switching occurs then you’ll see this in Visual Studio 2010:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_5dD_rBQSs2o/TY_mlDX5XLI/AAAAAAAABBg/2gF8Sr0OdbQ/s1600-h/image%5B112%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_5dD_rBQSs2o/TY_mlidiisI/AAAAAAAABBk/zTIJdCXHs2Q/image_thumb%5B58%5D.png?imgmax=800" width="447" height="171" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Which means I have to wait for the solution to close, for the get operation to complete and then I have to manually reopen the solution.&amp;#160; It’s easier than closing the solution in one folder and reopening the solution from another folder, but it’s still somewhat annoying.&lt;/p&gt;  &lt;p&gt;If on the other hand I do the branch remapping from the command line then I simply get this:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_5dD_rBQSs2o/TY_mmQcPUUI/AAAAAAAABBo/rdljzH9AU9M/s1600-h/image%5B115%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_5dD_rBQSs2o/TY_mnjFWQpI/AAAAAAAABBs/Uu3nuTiKJf8/image_thumb%5B61%5D.png?imgmax=800" width="480" height="164" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Visual Studio just prompts to reload open files that have changed, which makes the switching process much easier since I only can just click yes to all and be done.&amp;#160; Note that if project or solution files have changed you’ll be prompted to reload them, but there’s no simple way around this.&lt;/p&gt;  &lt;p&gt;Oh, one other reason for the command line usage is that at times Visual Studio gets confused about the workspace mapping changes and can sometimes block the switch by showing the “already mapped” error we saw earlier.&amp;#160; The command line has no such problems that I’ve seen.&lt;/p&gt;  &lt;h2&gt;Hang On! What If We Have Pending Changes?&lt;/h2&gt;  &lt;p&gt;Fair enough.&amp;#160; Let’s see what happens.&lt;/p&gt;  &lt;p&gt;Assume we’re currently on the integration branch and that we have edited a file.&amp;#160; The pending changes window shows this pending edit.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_5dD_rBQSs2o/TY_mosSZRrI/AAAAAAAABBw/ay7qffcEm2o/s1600-h/image%5B119%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_5dD_rBQSs2o/TY_mpRP75KI/AAAAAAAABB0/1nW6aX0ah58/image_thumb%5B65%5D.png?imgmax=800" width="484" height="173" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;If we now switch to the story branch we get a conflict:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_5dD_rBQSs2o/TY_mqPFPX1I/AAAAAAAABB4/yyHOFrQEULw/s1600-h/image%5B121%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_5dD_rBQSs2o/TY_mqyxwTpI/AAAAAAAABB8/lcliO-2WoQM/image_thumb%5B67%5D.png?imgmax=800" width="484" height="196" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;So at this point we do the obvious and click “Checkout File and AutoMerge”, right?&amp;#160; Well, not so fast!&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_5dD_rBQSs2o/TY_mro4EKxI/AAAAAAAABCA/HoULa8LTOzs/s1600-h/image%5B123%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_5dD_rBQSs2o/TY_msYT0qqI/AAAAAAAABCE/o9dFtA4ylCU/image_thumb%5B69%5D.png?imgmax=800" width="347" height="214" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;This is because the file hasn’t been downloaded to our workspace yet (remember we’re switching our workspace around). Here’s what we see in source control explorer if we want proof:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_5dD_rBQSs2o/TY_mt1UkdYI/AAAAAAAABCI/5l3SqJ49mQE/s1600-h/image%5B125%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_5dD_rBQSs2o/TY_muhS3DKI/AAAAAAAABCM/AR0Jyc451XU/image_thumb%5B71%5D.png?imgmax=800" width="484" height="88" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;I’d much rather the pending changes window didn’t show the unusable option, but that aside, we grudgingly take the only choice available to us and overwrite the local file.&amp;#160; Our changes are wiped out and the file is now exactly as it is in the story branch we just switched to.&lt;/p&gt;  &lt;p&gt;At this point if we look at the pending changes window we see this…&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_5dD_rBQSs2o/TY_mvO8zr_I/AAAAAAAABCQ/4rNfxVdZxbQ/s1600-h/image%5B126%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_5dD_rBQSs2o/TY_mwLAIq0I/AAAAAAAABCU/dFsMxSBDO6o/image_thumb%5B72%5D.png?imgmax=800" width="484" height="181" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Huh? What’s up with that? TFS is saying there’s a pending change on our file that we just overwrote, but this is the pending change for our original branch isn’t it?&amp;#160; If we check in at this point would we make a mess in our original branch.&amp;#160; Urgh!&lt;/p&gt;  &lt;p&gt;What if we now didn’t look and we went and edited EngineTest.cs in our story branch?&amp;#160; If we do and we refresh the pending changes window we see this:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_5dD_rBQSs2o/TY_mwhP99-I/AAAAAAAABCY/dTwNbZjRtzo/s1600-h/image%5B127%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_5dD_rBQSs2o/TY_mxZL6L0I/AAAAAAAABCc/oGqcQbcBilw/image_thumb%5B73%5D.png?imgmax=800" width="484" height="168" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Ah! So the refresh of pending changes shows that we not only have a change in our current story branch but that we also have a pending change for the integration branch still hanging around, but that the integration branch change isn’t mapped to a local file.&lt;/p&gt;  &lt;p&gt;I should probably check that integration change in so things don’t get confusing.&amp;#160; If we switch back to the integration branch, what happens?&amp;#160; Why, we get a merge conflict of course!&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_5dD_rBQSs2o/TY_mxwyY05I/AAAAAAAABCg/gGF04s-jL7U/s1600-h/image%5B130%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_5dD_rBQSs2o/TY_myliFgNI/AAAAAAAABCk/oUn7jKFX-qQ/image_thumb%5B76%5D.png?imgmax=800" width="484" height="176" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Well, we’d better overwrite the local file since we it currently contains changes for the story branch not our integration branch&lt;/p&gt;  &lt;p&gt;Unfortunately if we do so, our local changes will now be gone.&amp;#160; The pending changes we had floating about somewhere are toast!&amp;#160; In fact if we try and commit a file that is not currently mapped we get a dialog like this:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_5dD_rBQSs2o/TY_mzf74tuI/AAAAAAAABCo/xHE1raxtdHw/s1600-h/image%5B132%5D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_5dD_rBQSs2o/TY_m0IC3pZI/AAAAAAAABCs/woTqx8TezW8/image_thumb%5B78%5D.png?imgmax=800" width="494" height="244" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Hmm. OK. Not great.&lt;/p&gt;  &lt;h2&gt;Good Practices for In Place Branch Switching with TFS 2010&lt;/h2&gt;  &lt;p&gt;This means that in order for us to make branch switching as painless as possible with TFS, we should remember a few simple things:&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;Ensure there are no pending changes before switching.&amp;#160; Use shelvesets as needed to preserve changes.&lt;/li&gt;    &lt;li&gt;The command line is faster.&amp;#160; Don’t be afraid to use it.&lt;/li&gt;    &lt;li&gt;Ensure our workspace mappings are to folders marked as branches in TFS 2010 &lt;/li&gt; &lt;/ol&gt;  &lt;p&gt;And that’s it.&amp;#160; Go make the most of it, and if you bump into any edge cases feel free to post a comment about it.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-5977254732777513937?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/5977254732777513937/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2011/03/how-to-in-place-branch-switching-with.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/5977254732777513937'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/5977254732777513937'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2011/03/how-to-in-place-branch-switching-with.html' title='How To: In Place Branch Switching with TFS 2010'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/_5dD_rBQSs2o/TY_mU_zIehI/AAAAAAAABAE/f02wdkx1Jc8/s72-c/image_thumb%5B26%5D.png?imgmax=800' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-2922576209445928904</id><published>2011-03-15T15:06:00.001+11:00</published><updated>2011-03-15T15:06:37.516+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='community'/><title type='text'>Whither the User Group?</title><content type='html'>&lt;p&gt;I’d love to hear some opinions on this so please leave comment(s) below or &lt;a href="http://twitter.com/rbanks54" target="_blank"&gt;send me a tweet&lt;/a&gt;.&lt;/p&gt;  &lt;h3&gt;The Problem and Some Questions&lt;/h3&gt;  &lt;p&gt;In recent times in the Sydney area there have been a number of user group closures, extended breaks, sabbaticals and hiatuses.&amp;#160; The Sydney Mobile User Group &lt;a href="http://blog.rog42.net/2011/the-end-of-an-era/" target="_blank"&gt;has closed&lt;/a&gt;, the &lt;a href="http://www.sbtug.com/" target="_blank"&gt;Sydney Business and Technology User Group&lt;/a&gt; is on an extended break, the &lt;a href="http://thesaug.org/" target="_blank"&gt;Sydney Architecture User Group&lt;/a&gt; is &lt;a href="http://twitter.com/#!/glav/status/47405435392761856" target="_blank"&gt;going on sabbatical&lt;/a&gt;, the &lt;a href="http://www.ssw.com.au/ssw/NETUG/Sydney.aspx" target="_blank"&gt;Sydney .NET Users Group&lt;/a&gt; has been on an extended break recently, the &lt;a href="http://swiug.org.au/" target="_blank"&gt;Sydney Windows Infrastructure User Group&lt;/a&gt; is talking of resurrection after being dead for over a year and both the &lt;a href="http://www.sdnug.org/" target="_blank"&gt;Sydney Deep .NET User Group&lt;/a&gt; and the &lt;a href="http://sydbiz.org/" target="_blank"&gt;Sydney BizTalk group&lt;/a&gt; have been closed for quite some time now.&lt;/p&gt;  &lt;p&gt;Whilst there are still a number of groups in action, it seems that the traditional user group format is losing traction.&amp;#160; The question I pose to you is this: Why? And what can we do about it?&lt;/p&gt;  &lt;p&gt;Is it because the way we consume information has changed over the last few years?&amp;#160; For example I reach a much larger number of people via the &lt;a href="http://www.talkingshopdownunder.com/" target="_blank"&gt;Talking Shop Down Under&lt;/a&gt; podcast each week than I can via a face to face user group meeting.&amp;#160; Larger podcasts like &lt;a href="http://www.dotnetrocks.com/" target="_blank"&gt;Dot Net Rocks&lt;/a&gt; regularly get 60,000+ listeners per show, but a large user group only reaches 100 people once a month.&lt;/p&gt;  &lt;p&gt;Is it because our sense of community is now online.&amp;#160; Twitter, mailing lists, social coding, Q&amp;amp;A sites like stack overflow and more all help us form online communities and allow people to connect with each other in ways that we’ve not done in the past, and help us avoid the awkward &amp;quot;I have to introduce myself to people in the flesh” moments that a lot of developer types find really difficult and that are needed if people are going to really communicate at user groups.&lt;/p&gt;  &lt;p&gt;Is it because face to face meetings can be somewhat hit and miss?&amp;#160; There’s a time cost to attending a user group meeting and they occur at specific times.&amp;#160; Do people find the value of attending to low to make the time and inconvenience cost of being there in person worthwhile?&lt;/p&gt;  &lt;p&gt;Do virtual meetings work?&amp;#160; Virtual is great for learning and reflects the changing nature of how we consume information, but they’re not so great for interactive discussions. We’ve run virtual alt.net meetings in the past and it worked for a while, but it was less than what I’d hoped it would be. Can we improve how these are done and make them really valuable for people?&lt;/p&gt;  &lt;p&gt;Or is it something as simple as those who were running the group became burnt out or left the community and no one else stepped up to fill the leadership gap?&lt;/p&gt;  &lt;h3&gt;Some Thoughts&lt;/h3&gt;  &lt;p&gt;As a scrum person I like to regularly have retrospectives and “inspect and adapt”.&amp;#160; Add to that the alt.net approach of wanting to push the boundaries and challenge the status quo then it’s probably no surprise that I think it’s time to reinvent the user group as we know it.&amp;#160; The question now is how?&amp;#160; What would it look like?&lt;/p&gt;  &lt;p&gt;If we go to the source of all truth (Wikipedia!) and look at the &lt;a href="http://en.wikipedia.org/wiki/Online_community" target="_blank"&gt;online communities&lt;/a&gt; topic we see that there are a few stages that people go through to enter and participate in a community.&amp;#160; They are:&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;Peripheral (i.e. Lurker) – An outside, unstructured participation &lt;/li&gt;    &lt;li&gt;Inbound (i.e. Novice) – Newcomer is invested in the community and heading towards full participation &lt;/li&gt;    &lt;li&gt;Insider (i.e. Regular) – Full committed community participant &lt;/li&gt;    &lt;li&gt;Boundary (i.e. Leader) – A leader, sustains membership participation and brokers interactions &lt;/li&gt;    &lt;li&gt;Outbound (i.e. Elder) – Process of leaving the community due to new relationships, new positions, new outlooks &lt;/li&gt; &lt;/ol&gt;  &lt;p&gt;A user group generally targets the Insider/Boundary members, but there is often a barrier of entry for the lurkers and novices to overcome.&amp;#160; Attending a group when you don’t know anyone is hard.&amp;#160; Attending a group in a location you’ve not been to before is hard. Attending a group at night when family beckons is hard. People who have been attending for some time often forget about this and whilst they are more than welcoming of newcomers, most groups fall into behavioural patterns where the regular attenders talking to each other and the newbies sit quietly on the outer feeling semi-ostracized until someone drags them into a conversation.&lt;/p&gt;  &lt;p&gt;A virtual group on the other hand encourages the peripheral and inbound people by making the barrier to entry low, but without the “stickiness” of face to face communication few people transition to insiders and help build a true community.&lt;/p&gt;  &lt;p&gt;Another thing to consider is what motivates people to join a community, to remain in it and to participate in it.&amp;#160; Looking at the &lt;a href="http://en.wikipedia.org/wiki/Online_participation" target="_blank"&gt;online participation&lt;/a&gt; topic you see a few key ideas.&amp;#160; Ideas that are simply reflections of real world communities.&lt;/p&gt;  &lt;p&gt;Reciprocity – there’s got to be something worthwhile in return for the time cost of participating   &lt;br /&gt;Recognition – obvious enough    &lt;br /&gt;Influence – people want to have some effect on others in the community through high value contributions    &lt;br /&gt;Sense of community – it’s all about connections with other people.&amp;#160; We’re social beings, even if we’re introverts.&lt;/p&gt;  &lt;p&gt;User group meetings provide the opportunity for all of these ideas to be borne out, though online communities do so as well.&amp;#160; Facebook being the poster child for this.&amp;#160; That said, the connections made via facebook, twitter and other online communities are far looser that those made face to face.&amp;#160; There’s nothing like meeting people in person that you’ve only talked with online – it turns “friends” into friends, for example:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://twitter.com/#!/mabster/status/43201631675883520"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_5dD_rBQSs2o/TX7lvo2JvWI/AAAAAAAAA_c/o1_6b6KYJBg/image%5B5%5D.png?imgmax=800" width="484" height="217" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;h3&gt;A Proposal&lt;/h3&gt;  &lt;p&gt;So what can we do to reinvent user groups?&amp;#160; I don’t like talking about a problem without at least throwing some ideas for a solution out there to prompt conversation, so here’s a few tweets from some discussions this morning after I asked a few open ended questions about user groups…&lt;/p&gt;  &lt;p&gt;&lt;a href="http://twitter.com/#!/Rog42/status/47407665093165056"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_5dD_rBQSs2o/TX7lwVUAneI/AAAAAAAAA_g/Yu3iaupsux8/image%5B10%5D.png?imgmax=800" width="484" height="224" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://twitter.com/#!/rbanks54/status/47418079440936962"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_5dD_rBQSs2o/TX7lxQt9hlI/AAAAAAAAA_k/CbFvWNogaEE/image%5B20%5D.png?imgmax=800" width="484" height="208" /&gt;&lt;/a&gt;&lt;/p&gt;      &lt;p&gt;&lt;a href="http://twitter.com/#!/Rog42/status/47418058641379328"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_5dD_rBQSs2o/TX7lyKlgKQI/AAAAAAAAA_o/iT-OGV7dh_E/image%5B15%5D.png?imgmax=800" width="484" height="196" /&gt;&lt;/a&gt;&lt;/p&gt;    &lt;p&gt;&lt;a href="http://twitter.com/#!/Rog42/status/47419957851586560"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_5dD_rBQSs2o/TX7ly9Y0NYI/AAAAAAAAA_s/51k6QnD6XLE/image%5B25%5D.png?imgmax=800" width="484" height="220" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;So what do you think?&lt;/p&gt;  &lt;p&gt;Is it time to move the traditional monthly user group meeting to become a more social event with regular online presentations around the topics the group is interested in? It’s how the Perth Alt.Net group operates and I know of a number of other groups that are starting to do the same.&lt;/p&gt;  &lt;p&gt;If your user group did become a mixed virtual/physical group, how would you want to see the things run?&amp;#160; Social meetings at a bar, restaurant, someone’s house with a barbeque? Would you like the virtual meetings via Livemeeting? Livestream or Kyte.TV, etc? Or something else.&lt;/p&gt;  &lt;p&gt;Click the comments link below and let me know (or &lt;a href="http://twitter.com/rbanks54"&gt;send a tweet&lt;/a&gt; or start a thread on the &lt;a href="http://groups.google.com.au/group/ozaltdotnet"&gt;Australian Alt.Net mailing list&lt;/a&gt;).&amp;#160; I’m really curious to hear what you think.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-2922576209445928904?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/2922576209445928904/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2011/03/whither-user-group.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/2922576209445928904'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/2922576209445928904'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2011/03/whither-user-group.html' title='Whither the User Group?'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/_5dD_rBQSs2o/TX7lvo2JvWI/AAAAAAAAA_c/o1_6b6KYJBg/s72-c/image%5B5%5D.png?imgmax=800' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-930987107793745731</id><published>2011-03-03T12:24:00.002+11:00</published><updated>2011-03-05T04:34:51.576+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='TFS'/><title type='text'>Git-TFS Recent Improvements</title><content type='html'>&lt;p&gt;If you like using Git for local development work but work in a team environment where TFS in use then you’ll be glad to know that &lt;a href="https://github.com/spraints/git-tfs" target="_blank"&gt;the git-tfs project&lt;/a&gt; has been progressing well since I last posted about it.&lt;/p&gt;  &lt;p&gt;The best new feature is that now you can do a checkin direct from git-tfs instead of needing to shelve, and then check in via team explorer, making the whole process much, much smoother.&lt;/p&gt;  &lt;p&gt;Here’s an example of how things now work (assuming you have already cloned the TFS repository)&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_5dD_rBQSs2o/TW7tq7are5I/AAAAAAAAA-4/XRQ3hXXxf8k/s1600-h/image%5B3%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_5dD_rBQSs2o/TW7trx671tI/AAAAAAAAA-8/YB7X0BYap-8/image_thumb%5B1%5D.png?imgmax=800" width="549" height="350" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;When you run the &lt;em&gt;&lt;strong&gt;git tfs ct &lt;/strong&gt;&lt;/em&gt; (aka &lt;em&gt;checkintool&lt;/em&gt;) command, you will see the check in dialog so you can commit the changes.&amp;#160; Note that this is only supported with TFS2010.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_5dD_rBQSs2o/TW7tspv6R4I/AAAAAAAAA_A/8DGUscIpAOo/s1600-h/image%5B7%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_5dD_rBQSs2o/TW7ttsD6biI/AAAAAAAAA_E/rR-QvylQVX0/image_thumb%5B3%5D.png?imgmax=800" width="484" height="276" /&gt;&lt;/a&gt;&lt;/p&gt;    &lt;p&gt;I’ve got a small change in my fork which will hopefully be pulled in to the main project shortly that pulls the commit messages from git and populates the comment field of the check in.  UPDATE: This is now included in the main project so use spraint's version.&lt;/p&gt;  &lt;p&gt;Once the check in completes git-tfs then automatically pulls the changeset from the remote TFS server and merges the change locally to save you having to remember doing that yourself.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_5dD_rBQSs2o/TW7tuI9RNdI/AAAAAAAAA_I/N-iP1qiXRHg/s1600-h/image%5B11%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_5dD_rBQSs2o/TW7tu0sQECI/AAAAAAAAA_M/SEYX80DWfZM/image_thumb%5B5%5D.png?imgmax=800" width="465" height="101" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_5dD_rBQSs2o/TW7tvMdR62I/AAAAAAAAA_Q/unKEbOiGhf0/s1600-h/image%5B15%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_5dD_rBQSs2o/TW7tv1S92kI/AAAAAAAAA_U/8XLRN3uAUb4/image_thumb%5B7%5D.png?imgmax=800" width="429" height="101" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Finished!&amp;#160; Now we can get back to our normal development flow. This is so much easier.&lt;/p&gt;  &lt;p&gt;The advantage of having the checkin tool is that we can also associate our commit with work items in TFS as well as dealing with check in policies.&amp;#160; This is excellent!&lt;/p&gt;  &lt;p&gt;If you don’t want to use the check in tool UI, then you can use the git tfs checkin command and supply the –w option to associate to a work item.&amp;#160; Policy failures should result in the checkin failing.&lt;/p&gt;  &lt;p&gt;Note that for now, you will have to build git-tfs from source to get this functionality, but that shouldn’t be a problem for anyone wanting to use this tool :-)&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-930987107793745731?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/930987107793745731/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2011/03/git-tfs-recent-improvements.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/930987107793745731'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/930987107793745731'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2011/03/git-tfs-recent-improvements.html' title='Git-TFS Recent Improvements'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/_5dD_rBQSs2o/TW7trx671tI/AAAAAAAAA-8/YB7X0BYap-8/s72-c/image_thumb%5B1%5D.png?imgmax=800' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-6625288890736772034</id><published>2011-02-22T13:57:00.000+11:00</published><updated>2011-02-22T13:57:34.894+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='.net'/><category scheme='http://www.blogger.com/atom/ns#' term='database'/><title type='text'>Running Neo4j on Azure</title><content type='html'>&lt;p&gt;For those of you who don’t know what I’m talking about, &lt;a href="http://neo4j.org/" target="_blank"&gt;Neo4j&lt;/a&gt; is a graph DB, which is great for dealing with certain types of data such as relationships between people (I find it ironic that relational databases aren’t great for this) and it’s perfect for the project I’m working on at the moment.&lt;/p&gt;  &lt;p&gt;However this project also needs to be deployable on Azure, which means if I want a graph DB for storing data, I need something running on Azure.&amp;#160; I’d originally looked at Sones for doing this since it was the only one around that I knew could run on Azure, but my preference was to run Neo4j instead, because I know it a little better, and because I know that the community around it is quite helpful.&lt;/p&gt;  &lt;p&gt;The great news is that as of just recently (a week ago at time of writing) &lt;a href="http://blog.neo4j.org/2011/02/announcing-neo4j-on-windows-azure.html" target="_blank"&gt;Neo4j can now run on Azure&lt;/a&gt;! Happy days!! and “Perfect Timing”™ for my project.&amp;#160; I just had to try it out.&amp;#160; P.S. Feel free to go ahead and read the Neo4j blog post because it has a basic explanation of how to set it up, has some interesting notes on the implementation and also talks about the roadmap ahead.&lt;/p&gt;  &lt;p&gt;Here’s a simple step-by-step on how to get Neo4j running on you local Azure development environment:&lt;/p&gt;  &lt;p&gt;1. Download the Neo4j software from &lt;a href="http://neo4j.org/download/"&gt;http://neo4j.org/download/&lt;/a&gt;.&amp;#160; I grabbed the 1.3.M02 (windows) version.&lt;/p&gt;  &lt;p&gt;2. You’ll also need to &lt;a href="http://neo4j.org/get?file=Neo4j.Azure.Server.zip" target="_blank"&gt;download the Neo4j Azure project&lt;/a&gt;, which is linked to from the &lt;a href="http://blog.neo4j.org/2011/02/announcing-neo4j-on-windows-azure.html" target="_blank"&gt;Neo4j blog post&lt;/a&gt;. &lt;/p&gt;  &lt;p&gt;3. I’m assuming you have an up to date Java runtime locally, so go to your program files folder and zip up the jre6 folder.&amp;#160; Call it something creative like jre6.zip :-)&lt;/p&gt;  &lt;p&gt;4. If you haven’t already done so I’d also suggest you get yourself a utility for managing Azure blob storage.&amp;#160; I’m using the &lt;a href="http://azureblobstudio.codeplex.com/" target="_blank"&gt;Azure Blob Studio 2011&lt;/a&gt; &lt;a href="http://visualstudiogallery.msdn.microsoft.com/a5840d76-0dc7-4573-9e39-e08a28e1563e/" target="_blank"&gt;Visual Studio Extension&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;5. Spin up Visual Studio and load the Neo4j Azure solution.&amp;#160; Make sure the Azure project is the startup project&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_5dD_rBQSs2o/TWMlsck8CDI/AAAAAAAAA-M/tKEFsYomxXI/s1600-h/image%5B3%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_5dD_rBQSs2o/TWMls3X0aHI/AAAAAAAAA-Q/7D27NeBITbY/image_thumb%5B1%5D.png?imgmax=800" width="294" height="297" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;6. Next, upload both the Ne04j and JRE zip files you have to Azure blob storage.&amp;#160; Note that by default the Azure project looks for a neo4j container, so it’s best to create that container first:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_5dD_rBQSs2o/TWMltnj_G4I/AAAAAAAAA-U/l_16I4fb5n4/s1600-h/image%5B7%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_5dD_rBQSs2o/TWMluQYJfkI/AAAAAAAAA-Y/0t2RZxUGmPE/image_thumb%5B3%5D.png?imgmax=800" width="484" height="98" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;7. Now it’s time to spin this thing up!&amp;#160; Press the infamous F5 and wait for a while as Azure gets it act in order, deploys the app and starts everything up for you.&amp;#160; You’ll know things are up and running when a neo4j console app appears on your desktop.&amp;#160; You may also get asked about allowing a windows firewall exception, depending on your machine configuration.&lt;/p&gt;  &lt;p&gt;Behind the scenes, the Neo4j Azure app will pull both the java runtime and neo4j zip files down from blob storage, unzip them and then call java to start things up.&amp;#160; As you can imagine, this can take some time.&lt;/p&gt;  &lt;p&gt;If you have the local Compute Emulator running, then you should be able to see logging information indicating what’s happening, for example:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_5dD_rBQSs2o/TWMlvBu3azI/AAAAAAAAA-c/_t2Xribl5LA/s1600-h/image%5B11%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_5dD_rBQSs2o/TWMlwYBlaeI/AAAAAAAAA-g/2Gy-j9FNIf0/image_thumb%5B5%5D.png?imgmax=800" width="484" height="273" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;8. Once everything is up and running you should be able to start a browser up and point it to the administration interface.&amp;#160; On my machine that was &lt;a href="http://localhost:5100"&gt;http://localhost:5100&lt;/a&gt;.&amp;#160; You can check the port number to use by having a look at the top of the log for this:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_5dD_rBQSs2o/TWMlwzPbLMI/AAAAAAAAA-k/KY45A_Jhj98/s1600-h/image%5B15%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_5dD_rBQSs2o/TWMlxsyeCTI/AAAAAAAAA-o/-MmHFf-0oJA/image_thumb%5B7%5D.png?imgmax=800" width="484" height="40" /&gt;&lt;/a&gt;&lt;/p&gt;    &lt;p&gt;You should then see something like this:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_5dD_rBQSs2o/TWMlyVjZnyI/AAAAAAAAA-s/JwmXPSJCd04/s1600-h/image%5B23%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_5dD_rBQSs2o/TWMlzer61cI/AAAAAAAAA-w/4agoLSuXE20/image_thumb%5B11%5D.png?imgmax=800" width="484" height="392" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;That chart is showing data being added to the database.&amp;#160; Just what we want to see!&lt;/p&gt;  &lt;p&gt;So that’s it.&amp;#160; The server is up and running and it wasn’t too hard at all!&amp;#160; The Neo team is looking to make the whole process much simpler, but I think this is a great start and it is very welcome indeed!&lt;/p&gt;  &lt;p&gt;If you do have problems, check the locations in the settings of the Neo4jServerHost in the Azure project to make sure they match your local values.&amp;#160; You might just need to adjust them to get it working.&lt;/p&gt;  &lt;p&gt;P.S. If you have questions, then ask in the Neo4j forums.&amp;#160; I’m not an expert in any of this ;-)&amp;#160; I’m just really pleased to be able to see it running on Azure.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-6625288890736772034?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/6625288890736772034/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2011/02/running-neo4j-on-azure.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/6625288890736772034'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/6625288890736772034'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2011/02/running-neo4j-on-azure.html' title='Running Neo4j on Azure'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/_5dD_rBQSs2o/TWMls3X0aHI/AAAAAAAAA-Q/7D27NeBITbY/s72-c/image_thumb%5B1%5D.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-9041727981984499372</id><published>2011-02-14T14:39:00.000+11:00</published><updated>2011-02-14T14:39:58.706+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='.net'/><title type='text'>The Anti-Region Campaign</title><content type='html'>&lt;p&gt;For the benefit of my twitter followers who know I have an ongoing issue with the abuse of #regions in the code I have to work with, let me take some time to explain why I dislike them so much.&amp;#160; In fact, let this be considered yet another step in my ongoing anti-#region campaign! (similar to &lt;a href="http://www.antiifcampaign.com/" target="_blank"&gt;the anti-if campaign&lt;/a&gt;)&lt;/p&gt;  &lt;p&gt;This is not to say #regions aren’t useful, but that they are far too over used, and until people can consume #regions responsibly, prohibition laws should be enacted.&lt;/p&gt;  &lt;h4&gt;Got an Opinion? O‘course You Do!&lt;/h4&gt;  &lt;p&gt;Even without reading this post in full I’m sure you already have an opinion and like most people on internet time you’re likely to skim the first paragraph or two and then skip the rest, so to save you time here’s how to leave feedback:&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;em&gt;If You Agree with Richard:&lt;/em&gt;&lt;/strong&gt; If you too dislike the abuse of #regions then leave a comment on this post. If there’s enough support I might think about starting a proper anti-region campaign.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;em&gt;If You Think Richard is a Raving Lunatic: &lt;/em&gt;&lt;/strong&gt;If you think I’m over-reacting, slightly unhinged, don’t understand your special needs and why #regions make the world around you glow, or if you count yourself as a #region lover then I would ask that you leave your comments and feedback on this &lt;a href="http://goo.gl/QMET" target="_blank"&gt;post here&lt;/a&gt; instead.&lt;/p&gt;  &lt;h2&gt;Why Do #Regions Even Exist?&lt;/h2&gt;  &lt;p&gt;So why in the name of all things good and holy, do #regions exist in the first place?&amp;#160; Don’t they kill kittens?&amp;#160; Aren’t they pure evil?&lt;/p&gt;  &lt;p&gt;Let’s have a look at what the &lt;a href="http://msdn.microsoft.com/en-us/library/9a1ybwek.aspx" target="_blank"&gt;MSDN documentation&lt;/a&gt; says, shall we?&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;#region lets you specify a block of code that you can expand or collapse when using the &lt;a href="http://msdn.microsoft.com/en-us/library/td6a5x4s.aspx"&gt;outlining&lt;/a&gt; feature of the Visual Studio Code Editor. &lt;strong&gt;In longer code files, it is convenient to be able to collapse or hide one or more regions so that you can focus on the part of the file that you are currently working on.&lt;/strong&gt; &lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;OK.&amp;#160; That seems nice on the surface, but isn’t the code I’m working on a class file?&amp;#160; Don’t I need to see it in context? Hmm.&amp;#160; Let’s have a look at the reasons I don’t like #regions shall we:&lt;/p&gt;    &lt;h2&gt;Reason 1 – It Hides Code&lt;/h2&gt;  &lt;p&gt;So, according the the spec, it’s to hide chunks of code to help us focus more on the bits we’re changing.&amp;#160; Do you see the problem with that?&amp;#160; Why would I, as a developer, want to actually &lt;em&gt;see &lt;/em&gt;code?&amp;#160; Are you out of your mind!&amp;#160; Code is the last thing I want to see!&amp;#160; I want to write code, not read it!&lt;/p&gt;  &lt;p&gt;I should be able to look at a piece of class and get an understanding of it straight away and how it fits in with the rest of the class.&amp;#160; #regions can prevent that from happening.&lt;/p&gt;  &lt;p&gt;How can I actually tell what this code is doing? And apologies for the Hungarian notation VB code – I hope it doesn’t burn your eyes too badly!&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_5dD_rBQSs2o/TVii-sTAP0I/AAAAAAAAA9U/yfxw1ng7cSQ/s1600-h/image%5B3%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_5dD_rBQSs2o/TVii_Yy8dnI/AAAAAAAAA9Y/tkihb1znDCw/image_thumb%5B1%5D.png?imgmax=800" width="493" height="407" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;I know there’s something going on in there (the line numbers give me a hint), but can I know at first glance what it is? No chance!&amp;#160; This is a bad thing.&lt;/p&gt;  &lt;p&gt;P.S. One valid reason to allow #regions to exist is designer/code generated code.&amp;#160; &lt;strong&gt;&lt;em&gt;However &lt;/em&gt;&lt;/strong&gt;the availability of partial classes since Visual Studio 2005 means that a we’ve been able to pull all that designer generated crap out and keep it in a separate file for a long time now.&amp;#160; There’s no need to have that junk cluttering up my own hand crafted junk! #regions are redundant in this case.&lt;/p&gt;  &lt;h2&gt;Reason 2 – It Hides What You Think Might Be Code&lt;/h2&gt;  &lt;p&gt;I just love seeing this sort of thing:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_5dD_rBQSs2o/TVijAOKcynI/AAAAAAAAA9c/ikEmdnJM6XI/s1600-h/image%5B7%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_5dD_rBQSs2o/TVijA6brk2I/AAAAAAAAA9g/l7qB7_CpP3U/image_thumb%5B3%5D.png?imgmax=800" width="368" height="72" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;There’s gotta be something in that code there right?&amp;#160; Something important!&lt;/p&gt;  &lt;p&gt;So let’s open the region and have a look?&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_5dD_rBQSs2o/TVijBckhDrI/AAAAAAAAA9k/6azLbZjP2vw/s1600-h/image%5B11%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_5dD_rBQSs2o/TVijCUt4MRI/AAAAAAAAA9o/6aTSgMBXFRg/image_thumb%5B5%5D.png?imgmax=800" width="484" height="183" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Say what!? There’s nothing useful there at all?! It’s just commented out code?&lt;/p&gt;  &lt;p&gt;So the #region is “helping” us by hiding commented out code!&amp;#160; Surely I don’t need a region to help hide that from me, do I?&amp;#160; NO! Just delete the code! Don’t comment it out and wrap it in a #region!&amp;#160; What a great way to waste my time!&lt;/p&gt;  &lt;h2&gt;Reason 3 – It Hides Nothing At All&lt;/h2&gt;  &lt;p&gt;I don’t know wether to blame horrible coding standard that mandate the presence of specially names regions for segment class files or not but I’m sure you’ve never seen this little gem:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_5dD_rBQSs2o/TVijCwej8fI/AAAAAAAAA9s/ez8GLNJVlAw/s1600-h/image%5B23%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_5dD_rBQSs2o/TVijDahm8PI/AAAAAAAAA9w/AHRkP3KQ21Q/image_thumb%5B11%5D.png?imgmax=800" width="347" height="30" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Only to open the #region to see what’s inside and realise there’s nothing there!&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_5dD_rBQSs2o/TVijEFP6c2I/AAAAAAAAA90/ovbcnRUbLz8/s1600-h/image%5B27%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_5dD_rBQSs2o/TVijEkmqBhI/AAAAAAAAA94/Gz45Bdqa4ec/image_thumb%5B13%5D.png?imgmax=800" width="403" height="50" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;It’s like being given an empty box for your birthday.&amp;#160; All nicely wrapped up and making you wonder what special surprise is hiding inside only to open it and find that your kid brother has been playing a horrible, cruel joke on you.&lt;/p&gt;  &lt;p&gt;What’s even better is when you see a class with all the mandatory regions in it (like the code we saw in Reason 1) and yet when you open all the regions up, there’s just a single static method in there and the rest is blank. Or even better, the whole class is empty!&lt;/p&gt;  &lt;p&gt;Regions like this fail completely in their mission to help you focus on just the code you need to see, and instead add only noise and confusion to the understanding-what-the-class-does process, and make you doubt any other #regions you see elsewhere in the codebase.&lt;/p&gt;  &lt;h2&gt;Reason 4 – It Hides Just One Thing&lt;/h2&gt;  &lt;p&gt;What about this #region:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_5dD_rBQSs2o/TVijFFNa-CI/AAAAAAAAA98/KPm5xpwFVjg/s1600-h/image%5B15%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_5dD_rBQSs2o/TVijFzs1Y4I/AAAAAAAAA-A/PM3HMhlmSgg/image_thumb%5B7%5D.png?imgmax=800" width="361" height="35" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;There’s going to be some really complex code in there, right?&amp;#160; Why else would I have a region there, especially since regions are meant to hide code that might distract me!&lt;/p&gt;  &lt;p&gt;Open it up and what do I see?&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_5dD_rBQSs2o/TVijGXcOx_I/AAAAAAAAA-E/HuNtI8sX_Wg/s1600-h/image%5B19%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_5dD_rBQSs2o/TVijHIsbsdI/AAAAAAAAA-I/ReTes_xDSlM/image_thumb%5B9%5D.png?imgmax=800" width="574" height="152" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Oh wow! Just as well we used a region to hide a single property!&lt;/p&gt;  &lt;p&gt;Again, we’ve added noise to our code base for no reason.&lt;/p&gt;  &lt;h2&gt;Reason 5 – It Hides Butt-Ugly Code&lt;/h2&gt;  &lt;p&gt;So what about this situation?&amp;#160; You’re looking through a code base and see the method from hell and it’s associated demonic friends.&amp;#160; We don’t want to see that code do we?&amp;#160; No way! We want to hide that complexity, so let’s wrap it up in a region! Great thinking!&lt;/p&gt;  &lt;p&gt;This is what we are really doing with our code: &lt;strong&gt;We’re sweeping all that ugly under the carpet&lt;/strong&gt;.&lt;/p&gt;  &lt;p&gt;As a general principle, classes should have a &lt;a href="http://en.wikipedia.org/wiki/Single_responsibility_principle" target="_blank"&gt;single responsibility&lt;/a&gt; and methods should be 20 lines of code or less so they stay readable.&amp;#160; If methods are longer than that then you’re likely working at multiple levels of abstraction in the one method or trying to do too much, and you should consider refactoring your code to improve it’s design and readability.&lt;/p&gt;  &lt;p&gt;It’s better to keep the ugly visible so that you can deal with it.&amp;#160; Hiding bad code behind regions only serves to make bad code acceptable and it discourages efforts for improving it.&lt;/p&gt;  &lt;h2&gt;Reason 6 – It Hides Copy/Paste Efforts&lt;/h2&gt;  &lt;p&gt;The &lt;a href="http://en.wikipedia.org/wiki/Don't_repeat_yourself" target="_blank"&gt;DRY principle&lt;/a&gt; means logic should exist in one place and one place only, not be repeated all over the place, because a change to the logic means finding all the spots that logic exists and fixing them all.&amp;#160; It’s much easier to make a fix once than it is to fix it in multiple places.&lt;/p&gt;    &lt;p&gt;If I have a code base covered in regions I can’t tell if one section of code is similar to any other code I’ve seen before, because in all likelihood I’ve not seen it!&amp;#160; It’s probably been hidden away behind #region statements that mask all of the complexity and character of the code, instead making it a big block of beige.&lt;/p&gt;  &lt;p&gt;Here’s what code looks like when #regions have been liberally applied to it:&lt;/p&gt;  &lt;p&gt;&lt;img src="http://gallery.burrowowl.net/index.php?q=/image/15472.jpg" width="360" height="190" /&gt;&lt;/p&gt;  &lt;p&gt;It all looks the same.&amp;#160; And you’re not sure if it’s laughing at you or about to turn on you!&lt;/p&gt;      &lt;h2&gt;What About Grouping Your Code?&lt;/h2&gt;  &lt;p&gt;Let’s say you think #regions are great because they allow you to group your code and hide all those messy internal field and property declarations.&amp;#160; If you’ve only got a handful of them, do you really need the #region?&amp;#160; Probably not.&amp;#160; There’s not much to hide.&lt;/p&gt;  &lt;p&gt;If you’ve got hundreds of the suckers do you need a #region?&amp;#160; Maybe – though at that point you should be considering the design of your system.&amp;#160; Do you really need a class with that many properties or fields?&amp;#160; Can you alter the design to improve things? Is your class doing too much?&amp;#160; That’s a judgment call you’ll have to make.&lt;/p&gt;  &lt;p&gt;My gripe is with #regions is not with the directive in and of itself, but with it’s abuse.&amp;#160; If a #region is being used as I’ve outlined in the reasons above, then it’s time to stop, think about your code and why you’re using a #region and to then do yourself a favour.&amp;#160; Remove the #region and start writing better code instead.&lt;/p&gt;  &lt;p&gt;That will be all.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-9041727981984499372?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/9041727981984499372/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2011/02/anti-region-campaign.html#comment-form' title='19 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/9041727981984499372'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/9041727981984499372'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2011/02/anti-region-campaign.html' title='The Anti-Region Campaign'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/_5dD_rBQSs2o/TVii_Yy8dnI/AAAAAAAAA9Y/tkihb1znDCw/s72-c/image_thumb%5B1%5D.png?imgmax=800' height='72' width='72'/><thr:total>19</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-6100550258925135650</id><published>2011-02-10T23:10:00.001+11:00</published><updated>2011-02-10T23:10:30.712+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='presentations'/><title type='text'>Presenting at CodeProject’s Agile Virtual Tech Summit</title><content type='html'>&lt;p&gt;The title does give it away a little I suppose…&amp;#160; I’ll be presenting two sessions at the &lt;a href="http://www.virtualtechsummits.com/Register.aspx?eventID=7" target="_blank"&gt;Agile Virtual Tech Summit&lt;/a&gt; run by the Code Project.&amp;#160; I’m quite honoured to be asked to present and for reference in my 2 sessions I’ll be keynoting the Agile track with a talk on Agile from a Developers Perspective, with the other session being about Lessons Learned from Agile Implementations.&lt;/p&gt;  &lt;p&gt;You can &lt;a href="http://www.virtualtechsummits.com/Register.aspx?eventID=7" target="_blank"&gt;register for the free event&lt;/a&gt; on the site and the summit will get underway on Feb 23rd, 2011 at 12pm ET (for those in Australia that’s a 4AM start on the 24th so don’t forget the coffee!)&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-6100550258925135650?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/6100550258925135650/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2011/02/presenting-at-codeprojects-agile.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/6100550258925135650'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/6100550258925135650'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2011/02/presenting-at-codeprojects-agile.html' title='Presenting at CodeProject’s Agile Virtual Tech Summit'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-6591776477595923040</id><published>2011-02-05T00:10:00.000+11:00</published><updated>2011-02-05T00:10:59.905+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='TFS'/><category scheme='http://www.blogger.com/atom/ns#' term='how to'/><category scheme='http://www.blogger.com/atom/ns#' term='alm'/><title type='text'>Branch per Story Pattern and TFS</title><content type='html'>&lt;p&gt;The TFS branching guide is a very detailed run through of the various situations and scenarios under which branching can be performed and the different strategies that exist, however because the guide covers so many scenarios it’s difficult for some people to know what approach they should follow, and when they look at some of the advanced diagrams they freak out at the complexity.&lt;/p&gt;  &lt;p&gt;For this post I’m going to keep it simple and just show one approach, the one that I feel is best for agile teams implementing using user stories.&amp;#160; It’s effectively the feature branch approach, and I’ve been tempted not to write this post, however I find that that feature-branch approach can be a little confusing for some people.&amp;#160; “What is a feature?” “Is it multiple stories?” “Is it an epic?” “Is it a bug fix?” “Should a feature live across multiple sprints?” These are all questions I hear and it’s probably because the terminology is a little too generic and becomes open to interpretation and thus misunderstanding.&amp;#160; So, let’s make it easy and use Agile terminology.&lt;/p&gt;  &lt;h2&gt;Why Story Branches&lt;/h2&gt;  &lt;p&gt;So before we get into it, what exactly is wrong with working in trunk or using a branch per sprint approach? If you can make it work then there is nothing wrong with that approach, but it does come with some with hidden dangers.&lt;/p&gt;  &lt;p&gt;Consider the following situation.&amp;#160; A team accepts 3 product backlog items for a sprint and only completes 2 of those items.&amp;#160; The third story was in progress when the sprint concluded but was far from complete and the team now has a section of unfinished code in their code base.&lt;/p&gt;  &lt;p&gt;What should the team do with that unfinished code? Should they go back and comment it out or delete it?&amp;#160; Maybe, but how will they be sure they got it all?&amp;#160; What if some of the changes they were making affected the system architecture and those changes had been used in the implementation of the other two stories? What if the code was also merged to another branch in preparation for a release?&lt;/p&gt;  &lt;p&gt;What if they take a different approach and try wrapping all changes in if-blocks during development to try and isolate the changes?&amp;#160; Sure, this can work.&amp;#160; But again it has challenges.&amp;#160; The team needs to maintain a set of variables or pre-processor tokens related to each item that are used to decide if a feature is available or not.&amp;#160; There’s also going to be a large number of if statements that will add noise to the code and in a complex system you can easily imagine how out of control this could get.&lt;/p&gt;  &lt;p&gt;What if they just ignore these challenges (or forget the code) and leave the unfinished code as is? At best they have code that is unused and simply adds bloat to the system, at worst it results in the system being released with broken functionality and open security attack vectors that have never been checked or tested.&lt;/p&gt;  &lt;p&gt;If we also remind ourselves that sprints should produce production ready, potentially releasable increments of the system, then we really, really don’t want to have work in progress code in the system at sprint conclusion.&lt;/p&gt;  &lt;p&gt;For all of these reasons we should ensure our development of a feature is isolated from the rest of the development team and only made generally available when complete and ready for integration testing. &lt;/p&gt;  &lt;p&gt;There’s an interesting side effect to taking this approach.&amp;#160; If we develop features in isolation and consider &lt;a href="http://en.wikipedia.org/wiki/Conway's_Law" target="_blank"&gt;Conway’s law&lt;/a&gt; which when paraphrased says that system architectures reflect the way the organisation communicates, then by nature the systems we develop using a branch per story model will likely be architected and designed as componentised and modular systems, mirroring the development approach we are using.&lt;/p&gt;  &lt;h2&gt;Downsides&lt;/h2&gt;  &lt;p&gt;What are the downsides, then? Surely the administration overhead goes up and chances of screwing up merges increases, and yes, there is an element of that, but it isn’t as bad as you think.&lt;/p&gt;  &lt;p&gt;Administration time goes up because we now have to remember to create a branch, and we have to remember to switch branches when we work on different stories in the same sprint.&amp;#160; Is this really an issue though? Maybe it is.&amp;#160; However maybe it’s a hidden blessing in disguise.&amp;#160; When working in an agile manner we want to minimise wasted effort and keep our work in progress as low as we can. We know that the costs of multi-tasking, working on different stories at the same time, means we’re go slower overall than if we just work on one story at a time because of the mental context switch and yet the temptation to multi-task is high, especially if the stories we work on are somewhat boring.&lt;/p&gt;  &lt;p&gt;By following a branch per story strategy we discourage ourselves from multitasking since the simple annoyance of switching to a solution file on a different branch gives us a natural prevention mechanism.&amp;#160; Note that this annoyance doesn’t work with DVCS systems like git and mercurial since branch switching is very quick and easy, but in TFS, SubVersion and similar branches are represented as folders and switching means swapping folders, and thus closing and re-opening the solution.&lt;/p&gt;  &lt;p&gt;What about merging? Branching is easy but a great deal of pain can be had in the merging of branches.&amp;#160; This pain is most often encountered when the two branches have diverged significantly.&amp;#160; However, in a branch-per-story model most branches are short lived and thus merging fairly straightforward.&amp;#160; It doesn’t mean merge conflicts can’t occur, just that the merges are likely to have small amounts of conflicts.&lt;/p&gt;  &lt;h2&gt;Visual Branching Model&lt;/h2&gt;  &lt;p&gt;So let’s have a look at what we’re doing with the branching model.&amp;#160; For each story in the sprint we work on we’re create a story branch by branching the main integration branch.&lt;/p&gt;  &lt;p&gt;Let’s say the team picks up three stories for the sprint.&amp;#160; We create the story branches and the team commences work: &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_5dD_rBQSs2o/TUv5oqPlrFI/AAAAAAAAA8M/qeu1-03VMVE/s1600-h/image%5B5%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: ; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_5dD_rBQSs2o/TUv5pz94OtI/AAAAAAAAA8Q/gBCtfTrIsGk/image_thumb%5B3%5D.png?imgmax=800" width="480" height="233" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Let’s say Story 1 gets completed first.&amp;#160; The code for story 1 is merged back to the integration branch, the integration code is checked and verified and when all the tasks for the story are complete the branch is killed off.&lt;/p&gt;  &lt;p&gt;Next, Story 2 is completed so the developers pull from the integration branch, merge the changes, confirm things are OK locally, then push their changes back to the integration branch.&amp;#160; Again, the developers check the code in the integration branch is OK, and when it is the story 2 branch is killed off.&lt;/p&gt;  &lt;p&gt;Finally Story 3 is code complete.&amp;#160; Again, the developers pull the branch code down to their story branch, do the merge and make sure it’s OK.&amp;#160; When it looks good they push their changes back to the integration branch, make sure the integration branch is OK and kill of the Story 3 branch.&lt;/p&gt;  &lt;p&gt;Pretty simple process, right?&lt;/p&gt;  &lt;h2&gt;Doing it with TFS&lt;/h2&gt;  &lt;p&gt;So, enough with the explanations let’s see how we do this with TFS.&lt;/p&gt;  &lt;p&gt;Creating branches is easy enough, but how do you name the branches?&amp;#160; We don’t want to try and embed the story name in the branch name since that will increase folder length and makes it far more likely that we’ll hit the file path length limit (yes, there is one and it’s smaller than you think).&lt;/p&gt;  &lt;p&gt;The strategy I prefer is simple.&amp;#160; Because we are using TFS and stories are just TFS work items, I like to use the story number as the branch name.&amp;#160; Here’s an example:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_5dD_rBQSs2o/TUv5rKgAfjI/AAAAAAAAA8U/QV3ksgSjMh0/s1600-h/image%5B10%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: ; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_5dD_rBQSs2o/TUv5sMKELFI/AAAAAAAAA8Y/GvoOv48HFl0/image_thumb%5B6%5D.png?imgmax=800" width="176" height="117" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Now let’s do some work on story 2639 (aka “Make this system more awesome!”) and check it in. Now we need to make sure that we’re on par with the integration branch by doing a merge.&amp;#160; We simply right click the Integration branch and select Merge:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_5dD_rBQSs2o/TUv5tHqpXjI/AAAAAAAAA8c/Mv0GgVUw5ck/s1600-h/image%5B15%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: ; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_5dD_rBQSs2o/TUv5uLv7PEI/AAAAAAAAA8g/h5LTBgAqCaA/image_thumb%5B9%5D.png?imgmax=800" width="434" height="102" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Then merge to the appropriate story branch&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_5dD_rBQSs2o/TUv5u-oKt5I/AAAAAAAAA8k/7zwW0jYu24c/s1600-h/image%5B19%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: ; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_5dD_rBQSs2o/TUv5wFpFkWI/AAAAAAAAA8o/SdbWd_83JJk/image_thumb%5B11%5D.png?imgmax=800" width="484" height="270" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Select the latest version to make sure we’re current and hit the button&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_5dD_rBQSs2o/TUv5xWNyzfI/AAAAAAAAA8s/cO-z0J7gyTo/s1600-h/image%5B23%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: ; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_5dD_rBQSs2o/TUv5yUu3kyI/AAAAAAAAA8w/PKC8bDR2QTg/image_thumb%5B13%5D.png?imgmax=800" width="345" height="144" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;It appears there are no changes to merge.&amp;#160; Excellent, we’re current.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_5dD_rBQSs2o/TUv5zDlIvFI/AAAAAAAAA80/to0up-b7mtk/s1600-h/image%5B27%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: ; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_5dD_rBQSs2o/TUv51KXmygI/AAAAAAAAA84/IyOUXBHu1Ws/image_thumb%5B15%5D.png?imgmax=800" width="240" height="139" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;So now we merge our changes from the story to the integration branch (same process, just start by right-clicking the story branch).&amp;#160; Also, don’t forget that merges are made as local changes and still have to be committed, so add a check in comment and do just that.&lt;/p&gt;  &lt;p&gt;Wait for the integration build to pass and check the acceptance tests pass to let us know that the story is complete.&amp;#160; Once it is we can now kill the story branch.&amp;#160; To do this, simply right click the branch in source control explorer and select delete!&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_5dD_rBQSs2o/TUv52Me91BI/AAAAAAAAA88/iYIX3jTeoPs/s1600-h/image%5B36%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: ; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_5dD_rBQSs2o/TUv53E-kjsI/AAAAAAAAA9A/IleannrA734/image_thumb%5B20%5D.png?imgmax=800" width="348" height="190" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Again the delete’s are pending changes until we check them in.&amp;#160; So again, add a comment and check in the changes.&lt;/p&gt;  &lt;p&gt;Our branch hierarchy now changes from this:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_5dD_rBQSs2o/TUv53x3gljI/AAAAAAAAA9E/SMhJd5ik9Kc/s1600-h/image%5B42%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: ; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_5dD_rBQSs2o/TUv55AdxJPI/AAAAAAAAA9I/Hld8DrTzLAY/image_thumb%5B24%5D.png?imgmax=800" width="480" height="114" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;to this:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_5dD_rBQSs2o/TUv557Q9_hI/AAAAAAAAA9M/pIJXr4t5eUc/s1600-h/image%5B47%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: ; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_5dD_rBQSs2o/TUv57rHrbjI/AAAAAAAAA9Q/3NOwos6h4lg/image_thumb%5B27%5D.png?imgmax=800" width="433" height="139" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Nice and simple.&amp;#160; And not a lot of overhead.&lt;/p&gt;  &lt;p&gt;As for the other stories we follow the exact same procedure when they are complete:&lt;/p&gt;  &lt;p&gt;1. Make the changes needed for the story in the story branch   &lt;br /&gt;2. Pull and merge changes from the integration branch into the story branch    &lt;br /&gt;3. Deal with any merge conflicts    &lt;br /&gt;4. Check that the story is still OK    &lt;br /&gt;5. Push and merge the changes back up to the integration branch.&amp;#160; You shouldn’t have any merge conflicts for this merge.    &lt;br /&gt;6. Check that the Continuous Integration build passes    &lt;br /&gt;7. Run any other tests on the integrated code as required.&lt;/p&gt;  &lt;p&gt;And we’re done!&lt;/p&gt;  &lt;p&gt;When all stories are complete in the sprint there should be no story branches left. The only time this won’t be true is when the team finished the sprint with incomplete stories.&lt;/p&gt;  &lt;h2&gt;What About Bugs?&lt;/h2&gt;  &lt;p&gt;Bugs in an agile team are just the same as stories.&amp;#160; Create a bug fix branch each time you need to fix a bug just the same as we do for stories.&lt;/p&gt;  &lt;p&gt;About the only time you might not do this is when you are doing really simple bug fixes such as spelling mistakes where the changes are just cosmetic and there are no real logic or functional changes.&lt;/p&gt;  &lt;h2&gt;What About the Build Server?&lt;/h2&gt;  &lt;p&gt;So a question you may have is what do we do with builds and the build server? Answer: it’s up to you.&amp;#160; Given how short lived the story branches are likely to be it’s probably not worth creating build definitions for each story, but again, it’s completely up to you. Regardless of wether you have a build per story or not, you should have a continuous integration build tied to the integration branch.&amp;#160; When each story is complete and merged back to the integration branch a build should be triggered that compiles the code, runs all the unit tests and so forth to confirm that the code in the integration branch is OK and nothing went wrong during the merge.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-6591776477595923040?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/6591776477595923040/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2011/02/branch-per-story-pattern-and-tfs.html#comment-form' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/6591776477595923040'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/6591776477595923040'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2011/02/branch-per-story-pattern-and-tfs.html' title='Branch per Story Pattern and TFS'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/_5dD_rBQSs2o/TUv5pz94OtI/AAAAAAAAA8Q/gBCtfTrIsGk/s72-c/image_thumb%5B3%5D.png?imgmax=800' height='72' width='72'/><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-4092643738057856899</id><published>2011-01-31T10:01:00.001+11:00</published><updated>2011-01-31T10:01:14.246+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='scrum'/><title type='text'>How Do I Deal With “Mr Stinky” in Scrum?</title><content type='html'>&lt;p&gt;On one of the various mailing lists I’m part of this question came up (edited slightly)&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;Retrospectives teach us to say to team members “I think it would be good to improve such and such for the next sprint”.&amp;#160; How about sensitive subjects like “one of the guys speaks in an abrupt manner” or “one of the guys has bad breath”?&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;Body odour and stink breath, especially for the coffee drinkers amongst us can be a fairly common complaint.&amp;#160; If the team has to work closely with someone who has an odour issue then one of the hardest things they have to do with is hide the gagging every time Mr Stinky sits down next to them to discuss an issue.&amp;#160; As a scrum master it’s really easy to cop out of dealing with this problem and resort to, “let the team self-manage and figure it out for themselves” but that assumes that they have the skills to do so.&amp;#160; In the I.T. industry people are generally not so sensitive to other people’s feelings and leaving this issue alone for too long is likely to turn into a source of conflict within the team.&amp;#160; As scrum masters we need to facilitate good team work and step in when required to help resolve this problem.&lt;/p&gt;  &lt;p&gt;Having a subject like this pop up during a retro would be really bad for the team.&amp;#160; (I’d going to call it a smell/stench in team behaviour, but I apparently exceeded my dad humour quota for this month already).&amp;#160; The good thing is that it’s unlikely to happen as you’ll usually hear grumbling from individuals long before anyone on the team brings it up publicly.&amp;#160; People usually have more sense than to take the ambush intervention approach – breakfast radio DJ’s excluded.&amp;#160; The most common response is for the team to silently isolate the individual instead.&lt;/p&gt;  &lt;p&gt;Now personally, I’ve had to have a a few of those body odour/stink breath conversations with people before.&amp;#160; Sooo much fun. Not.&amp;#160; I feel awkward and nervous and dread the conversation each and every time, but sometimes it just has to be done.&lt;/p&gt;  &lt;p&gt;Unfortunately, there’s no “rules for dealing with sensitive personal subjects” in Scrum.&amp;#160; You’ll have to rely purely on soft skills and the approach to take very much depends on the personalities involved.&amp;#160; The only guidance I can really give is to put yourself in the other person's shoes first and think about how they might best like to be told bad news.&amp;#160; For most people that means privately and in a conciliatory, supportive manner.&amp;#160; And please, make sure you have enough self-awareness to realise that if you can’t deliver the message that way then you should go to someone else in the organisation who can and get them to step in for you.&amp;#160; First port of call being the HR department, assuming your organisation is large enough to have one.&lt;/p&gt;  &lt;p&gt;If this is something you’re facing at the moment, then I wish you good luck!&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-4092643738057856899?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/4092643738057856899/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2011/01/how-do-i-deal-with-mr-stinky-in-scrum.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/4092643738057856899'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/4092643738057856899'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2011/01/how-do-i-deal-with-mr-stinky-in-scrum.html' title='How Do I Deal With “Mr Stinky” in Scrum?'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-8464692031021957303</id><published>2011-01-15T13:09:00.001+11:00</published><updated>2011-01-15T13:09:58.140+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='community'/><title type='text'>[Ann] Newcastle Nerd Dinner, Jan 20</title><content type='html'>&lt;p&gt;A very quick public announcement: there’s a Nerd Dinner happening in Newcastle this Thursday, Jan 20.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://nerddinner.com/4026" target="_blank"&gt;Details are on the NerdDinner site&lt;/a&gt; so if you’re in the area why not come along.&amp;#160; It would be great to see you there.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-8464692031021957303?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/8464692031021957303/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2011/01/ann-newcastle-nerd-dinner-jan-20.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/8464692031021957303'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/8464692031021957303'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2011/01/ann-newcastle-nerd-dinner-jan-20.html' title='[Ann] Newcastle Nerd Dinner, Jan 20'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-3108778396002363650</id><published>2011-01-10T17:07:00.001+11:00</published><updated>2011-01-15T14:45:44.488+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='community'/><title type='text'>The Tyranny of OR for .NET Developers</title><content type='html'>&lt;p&gt;Over recent times I’ve noticed a handful of people writing blog posts about why they are leaving .NET and the Microsoft platform to move to Ruby on Rails, Python or something other technology.&amp;#160; The reasons vary from person to person but the general, recurring memes seem to encompass the following:&lt;/p&gt;  &lt;p&gt;1. Microsoft stifles innovation    &lt;br /&gt;2. Almost all .NET developers are below average in skill     &lt;br /&gt;3. Microsoft means Closed Source.&amp;#160; Other choices mean Open Source.    &lt;br /&gt;4. Newer technologies are superior to .NET     &lt;br /&gt;5. Visual Studio is bloatware and static typing sucks. Dynamic languages and Vim FTW!     &lt;br /&gt;6. Corporate development is evil and .NET means Corporate     &lt;br /&gt;7. Only in the open source world do people truly understand proper software engineering practices&lt;/p&gt;  &lt;p&gt;I’m not going to argue the points since these are peoples personal perspectives and the people who have them are more than entitled to them. However, I also think there are some other unmentioned reasons that people might leave .NET that could be described as one or all of the following:&lt;/p&gt;  &lt;p&gt;1. The new languages offer new frontiers to be explored.&amp;#160; They like the romance and adventure and opportunity that this offers.   &lt;br /&gt;2. Communities in these new languages are pushing themselves to do better and as a result have some great, innovative and creative people in them.&amp;#160; They want to be involved in what these people are doing.    &lt;br /&gt;3. They love learning and a new language offers many new learning opportunities.    &lt;br /&gt;4. Many startups are using the new languages and they prefer to work in the startup culture.    &lt;br /&gt;5. Because the new languages are still new, the average developer hasn’t yet heard about them.&amp;#160; The ratio of poor developers to competent developers is good.&amp;#160; It feels more like an elite club.    &lt;br /&gt;6. Switching because it seems to be the cool thing to do. To quote &lt;a href="http://troyhunt.com" target="_blank"&gt;Troy Hunt&lt;/a&gt; who I was talking to about this “It’s like all sorts of other social trends where there’s an attraction to being in a minority group”.&amp;#160; In other words: “If all those other people are switching then so should I.&amp;#160; I might be missing out on something!”&lt;/p&gt;  &lt;p&gt;What bothers me is not that people are switching since it’s their choice and well done for having the courage to change, but that these various exodus posts carry a sense that language and platform choice has to be an all or nothing decision.&amp;#160; Ah, &lt;a href="http://jarkkolaine.com/2008/08/23/experts-generalists-and-the-tyranny-of-or/" target="_blank"&gt;the tyranny of OR&lt;/a&gt;. The idea that you can’t have two seemingly contradictory ideas in place at the same time.&amp;#160; Jim Collins wrote about it in &lt;a href="http://www.jimcollins.com/article_topics/articles/good-to-great.html" target="_blank"&gt;Good to Great&lt;/a&gt; and talked about it &lt;a href="http://www.well.com/~bbear/collins.html#RTFToC10" target="_blank"&gt;in this interview&lt;/a&gt;:&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;“they get caught up in what we call &amp;quot;The Tyranny of the Or,&amp;quot; the belief that you cannot live with two seemingly contradictory ideas at the same time, that you can have change or stability, you can be conservative or bold, you can have low costs or high quality -- but never both. Our visionary companies all operate in what we call &amp;quot;The Genius of the And,&amp;quot; the ferocious insistence that they can and must have both at once.”&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;What happened to taking what you know &lt;strong&gt;&lt;em&gt;AND&lt;/em&gt;&lt;/strong&gt; adding more knowledge and skills to it?&amp;#160; Are we so limited that we lack the ability to keep knowledge of multiple platforms and technologies in our head at the same time.&amp;#160; Do we not know how to evaluate options and pick the right tool for the task at hand? Can we not change the community around us and improve it when we think there are problems?&lt;/p&gt;  &lt;p&gt;Why should it be .NET -or- a community that pushes boundaries?&amp;#160; &lt;br /&gt;Why should it be .NET -or- Mac/Linux development?    &lt;br /&gt;Why should it be .NET -or- Open Source development?     &lt;br /&gt;Why should it be .NET -or- A lightweight IDE?    &lt;br /&gt;Why should it be .NET -or- Dynamic language development?    &lt;br /&gt;Why should it be .NET -or- Learning new things?    &lt;br /&gt;Why should it be .NET -or- Code Crafstmanship?    &lt;br /&gt;Why should it be .NET -or- skilled coders?    &lt;br /&gt;Why should it be .NET -or- a startup/dotcom style company?&lt;/p&gt;  &lt;p&gt;Don’t make it a choice of this OR that, make it a case of this AND that AND that other thing AND oh, a bit of that over there.&lt;/p&gt;  &lt;p&gt;If you want to switch, go for it, it’s your decision, just don’t be ruled by the Tyranny of OR.&amp;#160; Try to embrace the Genius of AND.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-3108778396002363650?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/3108778396002363650/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2011/01/tyranny-of-or-for-net-developers.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/3108778396002363650'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/3108778396002363650'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2011/01/tyranny-of-or-for-net-developers.html' title='The Tyranny of OR for .NET Developers'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-6479163699976642744</id><published>2010-12-23T11:19:00.001+11:00</published><updated>2010-12-23T11:19:47.698+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='agile'/><category scheme='http://www.blogger.com/atom/ns#' term='scrum'/><title type='text'>Professional Scrum Master Course, Perth, Feb 2011</title><content type='html'>&lt;p&gt;Just a quick note letting you know that I’m running a PSM course in Perth. February 3rd &amp;amp; 4th, 2011.&amp;#160; If you’re interested in registering then head over to &lt;a title="http://scrummastercourse.eventbrite.com/" href="http://scrummastercourse.eventbrite.com/"&gt;http://scrummastercourse.eventbrite.com/&lt;/a&gt; for the details.&lt;/p&gt;  &lt;p&gt;I’d also encourage you to have a look at the details of &lt;a href="http://www.scrum.org/psmoverview/" target="_blank"&gt;the Scrum.org PSM course&lt;/a&gt; and the curriculum before registering.&amp;#160; And before you ask, no, you do not automatically get a certification for just attending.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-6479163699976642744?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/6479163699976642744/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2010/12/professional-scrum-master-course-perth.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/6479163699976642744'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/6479163699976642744'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2010/12/professional-scrum-master-course-perth.html' title='Professional Scrum Master Course, Perth, Feb 2011'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-2522461334145997468</id><published>2010-12-19T23:11:00.001+11:00</published><updated>2010-12-19T23:50:32.130+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='podcasting'/><title type='text'>One Year of Podcasting</title><content type='html'>&lt;p&gt;When I &lt;a href="http://www.richard-banks.org/2010/02/announcing-talking-shop-down-under.html" target="_blank"&gt;announced&lt;/a&gt; the Talking Shop Down Under podcast back in February earlier this year I wasn’t really sure what would happen.&amp;#160; I didn’t know if I’d be able to find enough people to talk to, wether I could keep up an (almost) weekly broadcast schedule and do it on a shoe-string budget, or even if the show would be interesting enough for people to listen to more than once, and yet here I am at the end of the year having just published the 40th episode for the year and with a slowly growing audience base.&lt;/p&gt;  &lt;p&gt;Wow!&amp;#160; I’m very, very surprised.&lt;/p&gt;  &lt;p&gt;I’m so appreciative of all the guests who have appeared on the show and have had a great time talking with each and every one of them.&amp;#160; I’ve also really enjoyed the audience interactions I’ve been having via Twitter, and lately with the &lt;a href="http://livestream.com/talkingshop" target="_blank"&gt;livestream broadcast&lt;/a&gt; of the recordings.&lt;/p&gt;  &lt;p&gt;This last one is something I’d like to try more of.&amp;#160; It’s obviously much rawer than the edited episodes, but I think having you listening in live while an episode is being recorded gives you a much greater chance to hear the questions asked that you want asked, not just whatever pops into my head at the time, so keep an eye on the &lt;a href="http://twitter.com/talking_shop" target="_blank"&gt;@talking_shop&lt;/a&gt; twitter account and when an episode is recording feel free to ask questions via the chat room.&amp;#160; We may even try for some dial-in talk-show style interactions in the new year.&lt;/p&gt;  &lt;p&gt;Anyway, here’s some stats for those who are interested.&amp;#160; As a note it’s always hard to know just how many people listen because subscribers to the feed and episode downloads don’t always mean the show was listened to, plus the download stats from archive.org (where I host episodes) seem to update at random times and not very often at that.&lt;/p&gt;  &lt;p&gt;Subscribers:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_5dD_rBQSs2o/TQ314C4HFRI/AAAAAAAAA7g/-2JoRzAXdHc/s1600-h/image%5B4%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: ; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_5dD_rBQSs2o/TQ314uPk_sI/AAAAAAAAA7k/gaZLRRFy2vE/image_thumb%5B2%5D.png?imgmax=800" width="522" height="168" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;The show is sitting at around 250 people who follow the feed and as you can see, the number of listeners has steadily grown throughout the year.&amp;#160; Those little blue spikers are when people are downloading/listening to the mp3 file.&lt;/p&gt;  &lt;p&gt;Most shows get around 300-350 listeners an episode, with the largest number so far being 700+ for the &lt;a href="http://www.talkingshopdownunder.com/2010/09/episode-29-scott-barnes-says-wpf-is.html" target="_blank"&gt;Scott Barnes/WPF is Dead&lt;/a&gt; episode, and next being the &lt;a href="http://www.talkingshopdownunder.com/2010/05/episode-12-webforms-mvp-with-tatham.html" target="_blank"&gt;WebForms MVP episode&lt;/a&gt; with Tatham Oddie.&lt;/p&gt;  &lt;p&gt;As would be expected, most people who follow the show are from Australia, which is to be expected since that’s the audience I’m targeting. The next 4 countries of the top 5 subscribing nations are:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;United States&lt;/li&gt;    &lt;li&gt;United Kingdom&lt;/li&gt;    &lt;li&gt;Belgium&lt;/li&gt;    &lt;li&gt;Mexico&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;And finally, for the curious, more people listen to the show via the RSS feed than they do from ITunes (though it’s a close run thing).&lt;/p&gt;  &lt;p&gt;So if you’ve been a regular listener this year then I tip my hat to you and say THANK YOU!&lt;/p&gt;  &lt;p&gt;P.S. Suggestions for guests and other show ideas are always welcome.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-2522461334145997468?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/2522461334145997468/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2010/12/one-year-of-podcasting.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/2522461334145997468'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/2522461334145997468'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2010/12/one-year-of-podcasting.html' title='One Year of Podcasting'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_5dD_rBQSs2o/TQ314uPk_sI/AAAAAAAAA7k/gaZLRRFy2vE/s72-c/image_thumb%5B2%5D.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-6324531965192966785</id><published>2010-12-14T12:05:00.001+11:00</published><updated>2010-12-14T12:32:40.431+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='.net'/><title type='text'>A (not so serious) C# 5.0 Language Suggestion</title><content type='html'>&lt;p&gt;Occasionally I run across less then optimal code as part of my day-to-day work, as do most people who deal with legacy code bases.&amp;#160; Even if your code is pure as the driven snow, if you’ve ever spent any time looking at the &lt;a href="http://thedailywtf.com/Series/CodeSOD.aspx" target="_blank"&gt;Code Snippet of The Day&lt;/a&gt; on the &lt;a href="http://thedailywtf.com/" target="_blank"&gt;Daily WTF&lt;/a&gt; then you’ll have seen this sort of thing before:&lt;/p&gt;&lt;pre class="brush: c#;"&gt;           for (int i = 0; i &amp;lt; 200; i++)&lt;br /&gt;            {&lt;br /&gt;                try { someCollection.RemoveAt(42); }&lt;br /&gt;                catch { }&lt;br /&gt;            }&lt;/pre&gt;&lt;p&gt;Yes, it’s the classic Try, Catch, Swallow pattern in action.&amp;#160; If there’s a problem in there, we put our hands in the air like we just don’t care.&lt;/p&gt;&lt;p&gt;So today one of my fellow Readify colleagues, Ivan Hamilton, suggested a new keyword be added to C# 5.0 to help all those developers out there who are constantly writing this sort of thing.&amp;#160; Behold the OBVLIVIOUS keyword!&lt;/p&gt;&lt;pre class="brush: c#;"&gt;           for (int i = 0; i &amp;lt; 200; i++)&lt;br /&gt;            {&lt;br /&gt;                oblivious { someCollection.RemoveAt(42); }&lt;br /&gt;            }&lt;/pre&gt;&lt;p&gt;Sheer genius!&amp;#160; That code exhibits the same behaviour and is now so much more readable!&lt;/p&gt;&lt;p&gt;If you were on the C# team, what syntactic sugar would you add to the language?&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-6324531965192966785?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/6324531965192966785/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2010/12/not-so-serious-c-50-language-suggestion.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/6324531965192966785'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/6324531965192966785'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2010/12/not-so-serious-c-50-language-suggestion.html' title='A (not so serious) C# 5.0 Language Suggestion'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-4965285930262999781</id><published>2010-12-01T08:41:00.001+11:00</published><updated>2010-12-01T08:41:50.655+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='community'/><category scheme='http://www.blogger.com/atom/ns#' term='architecture'/><title type='text'>Udi Dahan at Sydney Alt.Net</title><content type='html'>&lt;p&gt;Last night at the &lt;a href="http://sydney.ozalt.net/" target="_blank"&gt;Sydney Alt.Net group&lt;/a&gt; we had &lt;a href="http://www.udidahan.com/" target="_blank"&gt;Udi Dahan&lt;/a&gt; for an open Q&amp;amp;A session where he talked about systems design and NServiceBus.&amp;#160; The session was live streamed at the time, and the now the recording is available for posterity.&lt;/p&gt; &lt;object width="480" height="295" id="lsplayer" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"&gt;&lt;param name="movie" value="http://cdn.livestream.com/grid/LSPlayer.swf?channel=rbanks54&amp;amp;clip=pla_b1e9ed20-6a15-4213-98fb-c677c697a6b4&amp;amp;color=0xe7e7e7&amp;amp;autoPlay=false&amp;amp;mute=false&amp;amp;iconColorOver=0x888888&amp;amp;iconColor=0x777777"&gt;&lt;/param&gt;&lt;param name="allowScriptAccess" value="always"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;embed name="lsplayer" wmode="transparent" src="http://cdn.livestream.com/grid/LSPlayer.swf?channel=rbanks54&amp;amp;clip=pla_b1e9ed20-6a15-4213-98fb-c677c697a6b4&amp;amp;color=0xe7e7e7&amp;amp;autoPlay=false&amp;amp;mute=false&amp;amp;iconColorOver=0x888888&amp;amp;iconColor=0x777777" width="480" height="295" allowScriptAccess="always" allowFullScreen="true" type="application/x-shockwave-flash"&gt;&lt;/embed&gt;&lt;/object&gt;  &lt;div style="text-align: center; width: 480px; font-size: 11px; padding-top: 10px"&gt;Watch &lt;a title="live streaming video" href="http://www.livestream.com/?utm_source=lsplayer&amp;amp;utm_medium=embed&amp;amp;utm_campaign=footerlinks"&gt;live streaming video&lt;/a&gt; from &lt;a title="Watch rbanks54 at livestream.com" href="http://www.livestream.com/rbanks54?utm_source=lsplayer&amp;amp;utm_medium=embed&amp;amp;utm_campaign=footerlinks"&gt;rbanks54&lt;/a&gt; at livestream.com&lt;/div&gt;  &lt;p&gt;Enjoy!&lt;/p&gt;  &lt;p&gt;And don’t forget to subscribe to the &lt;a href="http://groups.google.com/group/ozaltdotnet" target="_blank"&gt;ozalt.net mailing list&lt;/a&gt; to stay in touch with other Aussies in the community wanting to improve the way they develop software and work with .Net as well as keeping up to date with other happenings at user groups around the country.&amp;#160; There’s also a set of RSS feeds on the main &lt;a href="http://ozalt.net" target="_blank"&gt;ozalt.net site&lt;/a&gt; you may find useful to keep in touch with other alt.net bloggers in Australia.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-4965285930262999781?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/4965285930262999781/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2010/12/udi-dahan-at-sydney-altnet.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/4965285930262999781'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/4965285930262999781'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2010/12/udi-dahan-at-sydney-altnet.html' title='Udi Dahan at Sydney Alt.Net'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-7532523152497458556</id><published>2010-11-30T12:11:00.000+11:00</published><updated>2010-11-30T12:11:46.765+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='.net'/><title type='text'>Needless If Statements</title><content type='html'>&lt;p&gt;I was looking through some code today and came across this:&lt;/p&gt;  &lt;div&gt;&lt;pre class="brush: c#;"&gt;context.HttpContext.Response.Clear();&lt;br /&gt;&lt;br /&gt;if (ImageFormat.Equals(ImageFormat.Bmp))&lt;br /&gt;    context.HttpContext.Response.ContentType = &amp;quot;image/bmp&amp;quot;;&lt;br /&gt;if (ImageFormat.Equals(ImageFormat.Gif))&lt;br /&gt;    context.HttpContext.Response.ContentType = &amp;quot;image/gif&amp;quot;;&lt;br /&gt;if (ImageFormat.Equals(ImageFormat.Icon))&lt;br /&gt;    context.HttpContext.Response.ContentType = &amp;quot;image/vnd.microsoft.icon&amp;quot;;&lt;br /&gt;if (ImageFormat.Equals(ImageFormat.Jpeg))&lt;br /&gt;    context.HttpContext.Response.ContentType = &amp;quot;image/jpeg&amp;quot;;&lt;br /&gt;if (ImageFormat.Equals(ImageFormat.Png))&lt;br /&gt;    context.HttpContext.Response.ContentType = &amp;quot;image/png&amp;quot;;&lt;br /&gt;if (ImageFormat.Equals(ImageFormat.Tiff))&lt;br /&gt;    context.HttpContext.Response.ContentType = &amp;quot;image/tiff&amp;quot;;&lt;br /&gt;if (ImageFormat.Equals(ImageFormat.Wmf))&lt;br /&gt;    context.HttpContext.Response.ContentType = &amp;quot;image/wmf&amp;quot;;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;p&gt;You’ve all seen this sort of thing before, right? And no doubt it’s cousin, the switch statement, is also very familiar to you.&lt;/p&gt;&lt;p&gt;The problem with that this sort of thing is that those if statements just aren’t required.&amp;#160; For amusement and/or education, go have a look at the &lt;a href="http://www.antiifcampaign.com/" target="_blank"&gt;Anti-If campaign&lt;/a&gt; to get a more detailed view as to why If statements are undesirable.&lt;/p&gt;&lt;p&gt;so when you see this sort of thing, take the few minutes you need to refactor your code to something like the following:&lt;/p&gt;&lt;div&gt;&lt;pre class="brush: c#;"&gt;var contentResponses = new Dictionary&amp;lt;ImageFormat, string&amp;gt;()&lt;br /&gt;                     {&lt;br /&gt;                         {ImageFormat.Bmp, &amp;quot;image/bmp&amp;quot;},&lt;br /&gt;                         {ImageFormat.Gif, &amp;quot;image/gif&amp;quot;},&lt;br /&gt;                         {ImageFormat.Icon, &amp;quot;image/vnd.microsoft.icon&amp;quot;},&lt;br /&gt;                         {ImageFormat.Jpeg, &amp;quot;image/jpeg&amp;quot;},&lt;br /&gt;                         {ImageFormat.Png, &amp;quot;image/png&amp;quot;},&lt;br /&gt;                         {ImageFormat.Tiff, &amp;quot;image/tiff&amp;quot;},&lt;br /&gt;                         {ImageFormat.Wmf, &amp;quot;image/wmf&amp;quot;}&lt;br /&gt;                     };&lt;br /&gt;&lt;br /&gt;context.HttpContext.Response.Clear();&lt;br /&gt;context.HttpContext.Response.ContentType = contentResponses[ImageFormat];&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;p&gt;You’ll have fewer lines of code and the person who next looks at your code will appreciate it.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-7532523152497458556?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/7532523152497458556/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2010/11/needless-if-statements.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/7532523152497458556'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/7532523152497458556'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2010/11/needless-if-statements.html' title='Needless If Statements'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-3689721453034644188</id><published>2010-11-23T19:34:00.001+11:00</published><updated>2010-11-23T19:34:55.240+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='TFS'/><category scheme='http://www.blogger.com/atom/ns#' term='how to'/><title type='text'>How to Build Linux Code with TFS 2010 Team Build</title><content type='html'>&lt;p&gt;With the release of Team Foundation Server 2010 and Team Explorer Everywhere Microsoft extended the reach of TFS beyond just the Microsoft ecosystem and provided a way for people doing Linux and Mac development to use TFS to meet their Application Lifecycle Management (ALM) needs.&lt;/p&gt;  &lt;p&gt;Whilst TFS works great for source control and work items for Linux development it doesn’t include a Linux specific build agent so many people think it can’t be done.&amp;#160; But that’s not quite true. And yes, before people point out the obvious, there are a number of excellent Linux specific build engines that can do automated builds from TFS source by calling the tf command line, but they don’t integrate into TFS in anywhere near the same way that Team Build does which is why Team Build a desirable option for many places.&lt;/p&gt;  &lt;p&gt;Anyway, back to the issue at hand, thanks to tools like PuTTY we can do remote shell calls to Linux boxes as part of the build process, and you we can also copy files to and from the Linux box as part of the same process which gives us the ability to compile on Linux and still get the compiled binaries placed in the drop location as per normal windows builds.&lt;/p&gt;  &lt;p&gt;For example I have a customer at the moment who is moving their Progress source code (yuck!) out of Roundtable and into TFS, and as part of that transition we’re moving the build from a custom set of shell scripts into Team Build.&amp;#160; Without going into the specifics of their particular needs, let’s have a quick look at some of the key ingredients needed to get Team Build successfully executing commands on a remote Linux server.&lt;/p&gt;  &lt;h3&gt;Install and Configure PuTTY on the Windows Build Agent&lt;/h3&gt;  &lt;p&gt;As mentioned before we’re going to use some of the PuTTY tools to connect to the remote Linux box.&amp;#160; If you haven’t already done so, &lt;a href="http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html" target="_blank"&gt;go grab the Windows PuTTY installer&lt;/a&gt; from the download page and install it on your build server.&lt;/p&gt;  &lt;p&gt;Now we need to configure PuTTY so it knows how to connect to the remote server.&amp;#160; We’re going to use SSH to do this, and since we don’t want to store passwords in our build scripts we want to &lt;a href="http://the.earth.li/~sgtatham/putty/0.58/htmldoc/Chapter8.html" target="_blank"&gt;use a public key for authentication&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;Login to your build server using the build service account.&amp;#160; Note: This is important! If you don’t do this then you will likely have issues the first time you run a build as ssh will prompt the first time you use a public key for authentication and the build will hang waiting for a response.&lt;/p&gt;  &lt;p&gt;Now open a command prompt, go to the install folder for PuTTY and run &lt;strong&gt;&lt;em&gt;puttygen.exe&lt;/em&gt;&lt;/strong&gt;.&amp;#160; A dialog will appear.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_5dD_rBQSs2o/TOt8jXDpwqI/AAAAAAAAA6k/7BBN4G2hSH4/s1600-h/image%5B2%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: ; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_5dD_rBQSs2o/TOt8kwNq8tI/AAAAAAAAA6o/UZsz1zez6Pw/image_thumb.png?imgmax=800" width="244" height="236" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Click Generate and move the mouse around a little as requested (such fun!) until you get a key generated.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_5dD_rBQSs2o/TOt8mXywFMI/AAAAAAAAA6s/Fe0RaJF-u7c/s1600-h/image%5B5%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: ; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_5dD_rBQSs2o/TOt8ogMiRfI/AAAAAAAAA6w/N9kXR-TBa_o/image_thumb%5B1%5D.png?imgmax=800" width="244" height="236" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Note that we’re intentionally leaving the passphrase blank so that we don’t get prompted for a password during the build process.&amp;#160; This of course means there is a potential security hole if someone attacks the windows build agent machine, so make sure that the account you will log in to on the Linux box is not a privileged account.&amp;#160; Save both your public and private keys.&lt;/p&gt;    &lt;p&gt;Next, login to your Linux box and open the &lt;em&gt;&lt;strong&gt;$HOME/.ssh/authorized_keys&lt;/strong&gt;&lt;/em&gt; file in vim and paste in the public key as a new entry in that file, then save the changes, close the file and log off.&lt;/p&gt;  &lt;p&gt;Now to test it and get through that first configuration prompt.&lt;/p&gt;  &lt;p&gt;From your windows command prompt run plink to do a listing of your home directory on the Linux box.&amp;#160; For example:&lt;/p&gt;  &lt;p&gt;&lt;em&gt;plink –batch –ssh –i privateKey.ppk &lt;/em&gt;&lt;a href="mailto:linuxBuildAccount@linuxServer"&gt;&lt;em&gt;linuxBuildAccount@linuxServer&lt;/em&gt;&lt;/a&gt;&lt;em&gt; “ls –l”&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;When prompted to store the key answer with a “y&amp;quot;es.&amp;#160; Once that’s been done once we won’t get asked again when we run automatic builds.&lt;/p&gt;  &lt;p&gt;Assuming this works, and you see something coming back then we’re right to move on.&lt;/p&gt;  &lt;h3&gt;Customising TFS Team Build to Build on Linux&lt;/h3&gt;  &lt;p&gt;From here it’s pretty simple and works much the same as the &lt;a href="http://www.richard-banks.org/2010/08/how-to-build-vb6-apps-with-tfs-team.html" target="_blank"&gt;customisation for VB6 builds&lt;/a&gt; I’ve posted about in the past.&amp;#160; This won’t be a complete blow-by-blow on how to do it – just enough information to cover the important parts you need to know.&lt;/p&gt;  &lt;p&gt;For all the remote Linux interactions we’re going to rely on the InvokeProcess workflow activity to make the calls we need for our build process.&lt;/p&gt;  &lt;p&gt;As a note, I usually take the existing Default Process template and gut it – removing most of the activities from after the workspace has synced (i.e. the get latest section) and the code has been pulled down to the build agent and use that as a starting point for building up the Linux build process.&lt;/p&gt;  &lt;p&gt;Regardless, once the code has been pulled down into the build agent’s workspace we have two choices for making the source available to Linux.&amp;#160; We could define a network share that points to the build agent’s $(Sources) folder and get Linux to build the sources using a UNC path (via samba), or alternatively we could use PuTTY’s pscp command to copy the sources to the Linux machine for local compilation and copy the compiled output back when the build completes.&amp;#160; You should do whatever you are more comfortable with, and since both have pros and cons it will be a matter of how your Linux build works that dictates the best approach.&lt;/p&gt;  &lt;p&gt;For this example let’s do a copy of code onto the Linux box and then call the compile.&lt;/p&gt;  &lt;p&gt;Begin by dragging an InvokeProcess activity into your build process workflow at an appropriate point:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_5dD_rBQSs2o/TOt8py98s3I/AAAAAAAAA60/nitTy5q-gNA/s1600-h/image%5B13%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: ; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_5dD_rBQSs2o/TOt8rPPHKNI/AAAAAAAAA64/4AwoE-utTSo/image_thumb%5B5%5D.png?imgmax=800" width="244" height="229" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;and set the properties of it as follows &lt;/p&gt;  &lt;p&gt;&lt;strong&gt;FileName:&lt;/strong&gt; &lt;em&gt;&amp;quot;&amp;quot;&amp;quot;&amp;quot; &amp;amp; Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86) &amp;amp; &amp;quot;\PuTTY\pscp.exe&amp;quot; &amp;amp; &amp;quot;&amp;quot;&amp;quot;&amp;quot;     &lt;br /&gt;&lt;/em&gt;&lt;strong&gt;Arguments:&lt;/strong&gt; &lt;em&gt;&amp;quot;-batch -scp -i &amp;quot;&amp;quot;&amp;quot;Path\to\\privateKey.ppk&amp;quot;&amp;quot; “ &amp;amp; SourcesDirectory &amp;amp;&amp;#160; “ linuxBuildAccount@linuxServer:/build/sources&amp;quot;&amp;quot;&amp;quot;&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;Don’t forget to check for error conditions when the task completes.&lt;/p&gt;  &lt;p&gt;Next drag another InvokeProcess activity into your workflow and this time use plink instead of pscp and call the command or script you need to do the compile. The following activity properties show an example:&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;FileName:&lt;/strong&gt; &lt;em&gt;&amp;quot;&amp;quot;&amp;quot;&amp;quot; &amp;amp; Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86) &amp;amp; &amp;quot;\PuTTY\plink.exe&amp;quot; &amp;amp; &amp;quot;&amp;quot;&amp;quot;&amp;quot;     &lt;br /&gt;&lt;/em&gt;&lt;strong&gt;Arguments:&lt;/strong&gt; &lt;em&gt;&amp;quot;-batch -ssh -i &amp;quot;&amp;quot;Path\to\privateKey.ppk&amp;quot;&amp;quot;&amp;#160; &lt;/em&gt;&lt;a href="mailto:linuxBuildAccount@linuxServer"&gt;&lt;em&gt;linuxBuildAccount@linuxServer&lt;/em&gt;&lt;/a&gt;&lt;em&gt; &amp;quot;&amp;quot;&amp;lt;command to run – e.g. make all&amp;gt;&amp;quot;&amp;quot;&amp;quot;&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;Again, don’t forget to check for errors.&lt;/p&gt;  &lt;p&gt;Finally when the build is done use another InvokeProcess activity to call pscp as shown above and copy any compiled output to the drop location.&amp;#160; For reference the drop location folder can be found using the &lt;a href="http://msdn.microsoft.com/en-us/library/microsoft.teamfoundation.build.server.builddetail.droplocation.aspx" target="_blank"&gt;BuildDetail.DropLocation&lt;/a&gt; property.&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;Hopefully this is enough to get you on your way to building your Linux applications via TFS 2010’s Team Build.&amp;#160; Good luck!&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-3689721453034644188?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/3689721453034644188/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2010/11/how-to-build-linux-code-with-tfs-2010.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/3689721453034644188'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/3689721453034644188'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2010/11/how-to-build-linux-code-with-tfs-2010.html' title='How to Build Linux Code with TFS 2010 Team Build'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/_5dD_rBQSs2o/TOt8kwNq8tI/AAAAAAAAA6o/UZsz1zez6Pw/s72-c/image_thumb.png?imgmax=800' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-2603013876113629998</id><published>2010-11-08T16:45:00.002+11:00</published><updated>2010-11-08T16:48:09.529+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='TFS'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><title type='text'>How to Use CodedUI Tests, Watin and MTM Together</title><content type='html'>&lt;p&gt;A customer I’m working with has placed a heavy investment in Watin testing over the years and with a recent move to TFS2010 they also wanted to take advantage of the new Microsoft Test Manager (MTM) feature and the ability to associate automated tests to test cases in MTM.&amp;#160; Here’s a quick how-to for those of you wanting to do the same thing.&lt;/p&gt;  &lt;h3&gt;Create A Test Case&lt;/h3&gt;  &lt;p&gt;First up, let’s create a test case:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_5dD_rBQSs2o/TNeOZa2-PJI/AAAAAAAAA6I/UQtzaXn5nwU/s1600-h/image%5B9%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: ; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_5dD_rBQSs2o/TNeOaw3i9FI/AAAAAAAAA6M/cHD_keWODvk/image_thumb%5B5%5D.png?imgmax=800" width="484" height="435" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Pretty simple – open browser and check the url in the address bar.&amp;#160; What I have also done with this test is use parameters to supply the data allowing testers to decide what values they want to test with.&amp;#160; This is better than the devs doing this, plus it makes for a much nicer UI for data driven tests than excel or CSV files do.&lt;/p&gt;  &lt;h3&gt;Create The CodedUI Watin Test&lt;/h3&gt;  &lt;p&gt;Next we need to create the automation for this test.&lt;/p&gt;  &lt;p&gt;Add a Coded UI test class to a test project but don’t go create any tests via the wizard that appears.&amp;#160; Just press cancel and then go to your test code and add a normal Watin test as per usual.&amp;#160; Oh, if you’re a person who normally deletes all that TestContext stuff, then you’ll need to leave it in place this time around – don’t delete it.&lt;/p&gt;  &lt;p&gt;Here’s an example:&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:f1a2aab0-9d82-4ecf-9674-a6562c405324" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: c#;"&gt;using Microsoft.VisualStudio.TestTools.UITesting;&lt;br /&gt;using Microsoft.VisualStudio.TestTools.UnitTesting;&lt;br /&gt;using WatiN.Core;&lt;br /&gt;&lt;br /&gt;namespace CodedUITesting&lt;br /&gt;{&lt;br /&gt;    [CodedUITest]&lt;br /&gt;    public class CodedUITest1&lt;br /&gt;    {&lt;br /&gt;    ...&lt;br /&gt;    [TestMethod]&lt;br /&gt;    [DataSource("Microsoft.VisualStudio.TestTools.DataSource.TestCase",&lt;br /&gt; "http://tfs2008-vm:8080/tfs/TemplateTrials;Scrum v5",&lt;br /&gt; "101", DataAccessMethod.Sequential)]&lt;br /&gt;    public void RunWatinTestParameterized()&lt;br /&gt;    {&lt;br /&gt; var url = TestContext.DataRow["url"].ToString();&lt;br /&gt; var result = TestContext.DataRow["result"].ToString();&lt;br /&gt; using (IE ie = new IE(url))&lt;br /&gt; {&lt;br /&gt;  Assert.AreEqual(result, ie.Url);&lt;br /&gt; }&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The things to note in this test are that the data source attribute is used to pull data from the TFS Test Case work item – specifically the URL of the team project is included, as it the test case id.&amp;#160; &lt;/p&gt;&lt;p&gt;In the test itself we pull data from the parameters in the test case using the TestContext.DataRow[“parameter'NameHere”].ToString() calls, and the rest is just normal Watin code.&lt;/p&gt;&lt;p&gt;If you have any issues with missing references make sure you reference Watin and Interop.ShDocVw and that both get copied to the output folder as part of the build.&lt;/p&gt;&lt;h3&gt;Attach the Automation&lt;/h3&gt;&lt;p&gt;The only thing we need to do once we have this test is to link it to the test case as it’s automation method.&amp;#160; Open the test case in Visual Studio, navigate to the Associated Automation tab and click the […] button to select the method.&amp;#160; You should see something like this once it’s done:&lt;/p&gt;&lt;p&gt;&lt;a href="http://lh3.ggpht.com/_5dD_rBQSs2o/TNeObvRVSLI/AAAAAAAAA6Q/1CQKgM1FIzU/s1600-h/image%5B11%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: ; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_5dD_rBQSs2o/TNeOcdZWeaI/AAAAAAAAA6U/7k5iQFYvQKA/image_thumb%5B7%5D.png?imgmax=800" width="484" height="257" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;h3&gt;Run The Test&lt;/h3&gt;&lt;p&gt;Assuming you have a lab environment with test agents installed you should then be able to trigger a new build and when it’s complete start an automated test run for that build and see everything working as you would expect.&lt;/p&gt;&lt;p&gt;Here’s a result from a run on my local TFS server:&lt;/p&gt;&lt;p&gt;&lt;a href="http://lh6.ggpht.com/_5dD_rBQSs2o/TNeOdbXRCvI/AAAAAAAAA6Y/tzTfj9AVeXw/s1600-h/image%5B15%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: ; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_5dD_rBQSs2o/TNeOezL4DmI/AAAAAAAAA6c/bj3R17U9NWk/image_thumb%5B9%5D.png?imgmax=800" width="484" height="456" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;I hope this helps!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-2603013876113629998?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/2603013876113629998/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2010/11/how-to-use-codedui-tests-watin-and-mtm.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/2603013876113629998'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/2603013876113629998'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2010/11/how-to-use-codedui-tests-watin-and-mtm.html' title='How to Use CodedUI Tests, Watin and MTM Together'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/_5dD_rBQSs2o/TNeOaw3i9FI/AAAAAAAAA6M/cHD_keWODvk/s72-c/image_thumb%5B5%5D.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-6201234251742236590</id><published>2010-10-28T16:24:00.001+11:00</published><updated>2010-10-28T16:24:34.563+11:00</updated><title type='text'>How To Stop Users Creating Work Items In TFS</title><content type='html'>&lt;p&gt;I regularly get asked if there is any way to prevent users creating certain types of work item in TFS – the most common scenario being stopping people who are using the work item only view from creating anything other than bugs.&lt;/p&gt;  &lt;p&gt;TFS still doesn’t let me hide blocked work item types from the users in the first place, but I can at least prevent users creating new work items of a certain type, which is a nice workaround.&lt;/p&gt;  &lt;p&gt;Here’s how:&lt;/p&gt;  &lt;p&gt;1. If you haven’t done so install the latest Team Foundation Power Tools for your appropriate TFS version.&amp;#160; If you are on TFS 2010 you’ll want the &lt;a href="http://visualstudiogallery.msdn.microsoft.com/en-us/c255a1e4-04ba-4f68-8f4e-cd473d6b971f" target="_blank"&gt;TFS 2010 September release&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;2. Start Visual Studio and use the Process Editor to open the work item type you want to edit   &lt;br /&gt;&lt;a href="http://lh4.ggpht.com/_5dD_rBQSs2o/TMkI8-IzeaI/AAAAAAAAA5g/T0VB05DCtuM/s1600-h/image%5B3%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: ; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_5dD_rBQSs2o/TMkI9_Y_QSI/AAAAAAAAA5k/nfrQfZ97oxE/image_thumb%5B1%5D.png?imgmax=800" width="484" height="89" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;3. Go to the workflow definition for the work item type and find the initial transition – it will be the one where the workflow moves from a blank state to an initial state:   &lt;br /&gt;&lt;a href="http://lh3.ggpht.com/_5dD_rBQSs2o/TMkI-gDrvbI/AAAAAAAAA5o/x-3mVEqFUik/s1600-h/image%5B8%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: ; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_5dD_rBQSs2o/TMkI_cZAIeI/AAAAAAAAA5s/ZZGzAUTZqRs/image_thumb%5B4%5D.png?imgmax=800" width="376" height="304" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;4. Double click the transition and in the properties locate the Transition Detail tab and the “Not” field. This field is used to exclude users or groups from being able to use this transition, so set the value as appropriate.&amp;#160; In the example I am excluding a dummy user from a local VM I have.   &lt;br /&gt;&lt;a href="http://lh4.ggpht.com/_5dD_rBQSs2o/TMkJAVJhipI/AAAAAAAAA5w/6beMhroJ1E4/s1600-h/image%5B12%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: ; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_5dD_rBQSs2o/TMkJBAfVe_I/AAAAAAAAA50/kpRZnrBbrjI/image_thumb%5B6%5D.png?imgmax=800" width="514" height="310" /&gt;&lt;/a&gt;&lt;/p&gt;      &lt;p&gt;5. Publish the work item type back to your TFS server and refresh team explorer to pick up the changes.&lt;/p&gt;  &lt;p&gt;Done!&lt;/p&gt;  &lt;p&gt;Let’s check it.&amp;#160; Connect as a blocked user and try and create a work item – in my example I excluded my dummy user from bugs.&amp;#160; Initially I see that I need to supply the Title, just as I had to before:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_5dD_rBQSs2o/TMkJB1zsGVI/AAAAAAAAA54/efSBhj7E6EM/s1600-h/image%5B21%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: ; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_5dD_rBQSs2o/TMkJCspSfCI/AAAAAAAAA58/rSaKkzlo2DI/image_thumb%5B11%5D.png?imgmax=800" width="359" height="122" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;However, once I supply a title I now see the following:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_5dD_rBQSs2o/TMkJDeI9sKI/AAAAAAAAA6A/DxM6yHGlXDA/s1600-h/image%5B23%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: ; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_5dD_rBQSs2o/TMkJEMEXMoI/AAAAAAAAA6E/BGgprUghbtk/image_thumb%5B13%5D.png?imgmax=800" width="484" height="173" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Notice that now the State field is showing as required, but I am now unable to set the state to a valid value which effectively prevents me creating the work item type.&lt;/p&gt;  &lt;p&gt;Hopefully that helps.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-6201234251742236590?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/6201234251742236590/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2010/10/how-to-stop-users-creating-work-items.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/6201234251742236590'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/6201234251742236590'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2010/10/how-to-stop-users-creating-work-items.html' title='How To Stop Users Creating Work Items In TFS'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_5dD_rBQSs2o/TMkI9_Y_QSI/AAAAAAAAA5k/nfrQfZ97oxE/s72-c/image_thumb%5B1%5D.png?imgmax=800' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-5382347465964362261</id><published>2010-10-22T16:32:00.001+11:00</published><updated>2010-10-22T16:32:26.699+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='development'/><title type='text'>Why does Git Merge Work Better than TFS Merge</title><content type='html'>&lt;p&gt;The question came up during a discussion on our internal mailing list as to why Git’s merge is seen as better than the TFS merge.&amp;#160; What better way to explain it than via a worked example:&lt;/p&gt;  &lt;h3&gt;TFS Example&lt;/h3&gt;  &lt;p&gt;Consider the following solution in TFS:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_5dD_rBQSs2o/TMEhsIScIFI/AAAAAAAAA4A/tNgNDYF-ftc/s1600-h/image%5B5%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: ; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_5dD_rBQSs2o/TMEhsn2EM_I/AAAAAAAAA4E/4Ek8LHW5pvs/image_thumb%5B2%5D.png?imgmax=800" width="205" height="178" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Now let’s branch it for two different teams.&amp;#160; Team A and Team B.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_5dD_rBQSs2o/TMEhtAzul1I/AAAAAAAAA4I/--G8dKo9p9c/s1600-h/image%5B19%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: ; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_5dD_rBQSs2o/TMEhtrukyxI/AAAAAAAAA4M/jJcj4IE1Gts/image_thumb%5B10%5D.png?imgmax=800" width="223" height="179" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Now let’s go into TeamA’s code and refactor SomeClass to have a better name.&amp;#160; Here’s the changeset that gets created.&amp;#160; Note that we’ve changed the .csproj and Program.cs as well since they both had references to SomeClass.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_5dD_rBQSs2o/TMEhuOkNxxI/AAAAAAAAA4Q/FeCN9Jv9ULc/s1600-h/image%5B26%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: ; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_5dD_rBQSs2o/TMEhuvQOcrI/AAAAAAAAA4U/pyPR2yKKS-U/image_thumb%5B15%5D.png?imgmax=800" width="484" height="155" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Meanwhile TeamB adds an extra property for some functionality they are adding:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_5dD_rBQSs2o/TMEhvVsQT6I/AAAAAAAAA4Y/tUcZYSsHUjY/s1600-h/image%5B24%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: ; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_5dD_rBQSs2o/TMEhv5nTS7I/AAAAAAAAA4c/FtD-Xm0cudM/image_thumb%5B13%5D.png?imgmax=800" width="455" height="128" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Team A then merges back to trunk.&amp;#160; When merging in Visual Studio you can only merge between branches that are directly related to each other.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_5dD_rBQSs2o/TMEhwWTudWI/AAAAAAAAA4g/wDe4Wkqr0po/s1600-h/image23.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; margin: ; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_5dD_rBQSs2o/TMEhxMOkirI/AAAAAAAAA4k/UiTH7jV-Bys/image_thumb15.png?imgmax=800" width="313" height="306" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Team B then wants to merge to trunk, but as Team A checked in first, they decide to follow good practices and pull down the latest changes from Trunk and merge them first.&amp;#160; Because the SomeClass file was renamed we now have a merge conflict as seen here:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_5dD_rBQSs2o/TMEhxnEgm8I/AAAAAAAAA4o/jKXBNRJ8Zrc/s1600-h/image%5B33%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: ; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_5dD_rBQSs2o/TMEhyetJY_I/AAAAAAAAA4s/cukAxAOEHy4/image_thumb%5B18%5D.png?imgmax=800" width="484" height="145" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;And until we fix it our solution is broken.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_5dD_rBQSs2o/TMEhy1IP7OI/AAAAAAAAA4w/ZYv3_8gAlTA/s1600-h/image%5B29%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: ; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_5dD_rBQSs2o/TMEhzYUeRcI/AAAAAAAAA40/S1fp2yuzttE/image_thumb%5B16%5D.png?imgmax=800" width="216" height="174" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;   &lt;p&gt;Visual Studio asks us what we want to do with the file name and we need to figure out what the right option is.&amp;#160; Giving choice like this to the user is unnecessary and can easily result in a big mess.&lt;/p&gt;    &lt;h3&gt;Git Example&lt;/h3&gt;    &lt;p&gt;Let’s do the same thing using git.&amp;#160; We start by creating a new project and making the two team branches.&amp;#160; Note how the branches all point to the same commit instead of creating copies of all the files. &lt;/p&gt; &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_5dD_rBQSs2o/TMEh0ODVRPI/AAAAAAAAA44/XxyoMyLOKs8/s1600-h/image%5B37%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: ; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_5dD_rBQSs2o/TMEh0rlUXXI/AAAAAAAAA48/_ITD6ELPQNU/image_thumb%5B20%5D.png?imgmax=800" width="484" height="402" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Now let’s refactor the TeamA code and add the property to TeamB as we did with the TFS example and commit the changes.&amp;#160; We see the branches now have some changes and are diverging from each other.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_5dD_rBQSs2o/TMEh1I9h88I/AAAAAAAAA5A/03ipAj-BwJ0/s1600-h/image%5B41%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: ; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_5dD_rBQSs2o/TMEh171woHI/AAAAAAAAA5E/QVbr2biPQ94/image_thumb%5B22%5D.png?imgmax=800" width="297" height="143" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Now let’s merge the TeamA changes to TeamB.&amp;#160; For consistency with the TFS example we’ll do it by merging TeamA to master, and then master to TeamB&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_5dD_rBQSs2o/TMEh2_FcaQI/AAAAAAAAA5I/3Lw_uu8Ryvk/s1600-h/image%5B49%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: ; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_5dD_rBQSs2o/TMEh3brxO2I/AAAAAAAAA5M/STUWDAIqHxM/image_thumb%5B26%5D.png?imgmax=800" width="457" height="429" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Note that the merge from TeamA to master is a fast-forward merge.&amp;#160; This happens because trunk hasn’t changed since TeamA branched so master can just be repositioned to the TeamA commit..&lt;/p&gt;  &lt;p&gt;You can see that in the second merge from master to TeamB an auto-merge occurred, and git automatically detected that the changed SomeClass.cs file and the new SomeBetterName.cs file are actually the same file (with an 84% certainty), so it performed a rename for us automatically.&lt;/p&gt;  &lt;p&gt;When we reload the solution in Visual Studio, everything looks fine, and we can get on with our development activities.&amp;#160; Here’s what it looks like in the repository viewer:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_5dD_rBQSs2o/TMEh4MNDtQI/AAAAAAAAA5Q/OEQciUqRUTo/s1600-h/image%5B53%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: ; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_5dD_rBQSs2o/TMEh48JUHxI/AAAAAAAAA5U/fNXvJQ7KUb4/image_thumb%5B28%5D.png?imgmax=800" width="484" height="357" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;As a note, with git we don’t have to go via the master branch to merge TeamA changes to TeamB, we could have just done git merge TeamA as shown here:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_5dD_rBQSs2o/TMEh5sCpOyI/AAAAAAAAA5Y/TZA9q8kCWbs/s1600-h/image%5B54%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: ; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_5dD_rBQSs2o/TMEh6c8rmWI/AAAAAAAAA5c/HE5ML-4Aj1M/image_thumb%5B29%5D.png?imgmax=800" width="484" height="235" /&gt;&lt;/a&gt;&lt;/p&gt;      &lt;p&gt;For reference, you don’t have to merge from TeamA to TeamB via Trunk in TFS either.&amp;#160; TFS has a baseless merge option that you can run via the command line, though when you do the branch history can get a little messed up.&lt;/p&gt;  &lt;p&gt;Here’s hoping that the next version of TFS will have a much better story around rename detection and version control in general.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-5382347465964362261?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/5382347465964362261/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2010/10/why-does-git-merge-work-better-than-tfs.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/5382347465964362261'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/5382347465964362261'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2010/10/why-does-git-merge-work-better-than-tfs.html' title='Why does Git Merge Work Better than TFS Merge'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_5dD_rBQSs2o/TMEhsn2EM_I/AAAAAAAAA4E/4Ek8LHW5pvs/s72-c/image_thumb%5B2%5D.png?imgmax=800' height='72' width='72'/><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-4175859893519632140</id><published>2010-10-16T13:11:00.001+11:00</published><updated>2010-10-16T13:11:48.252+11:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='development'/><title type='text'>Visual Studio and Deja Vu</title><content type='html'>&lt;p&gt;This a post about fonts, not echoes of memories, just in case you’re wondering.&amp;#160; Now, let me say from the outset that I like Consolas and think it’s a great font, but almost everyone I know who uses Visual Studio does so using either it or one of the other fixed width fonts supplied with windows.&lt;/p&gt;  &lt;p&gt;Given that the font is the thing I spend most time looking at, I wanted something a little different from the norm and that my eyes liked better so when I came across the &lt;a href="http://dejavu-fonts.org/wiki/Main_Page" target="_blank"&gt;Deja Vu&lt;/a&gt; font via Twitter I thought I’d give it a try.&amp;#160; And I must say, I’m very happy with it.&amp;#160; Here’s a screenie of it in action:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_5dD_rBQSs2o/TLkJz_LJDQI/AAAAAAAAA3g/w4e8UqFm93o/s1600-h/image%5B10%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: ; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_5dD_rBQSs2o/TLkJ0ewXYHI/AAAAAAAAA3k/eWr4qFJn6Ao/image_thumb%5B8%5D.png?imgmax=800" width="425" height="189" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;As you&amp;#160; can see it’s got dotted zero’s, good fixed width (mono spaced) characters, is sans serif and has quite a different look to Consolas, shown here for comparison&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_5dD_rBQSs2o/TLkJ1MQ157I/AAAAAAAAA3o/7lM8zdGCsbA/s1600-h/image%5B18%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: ; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_5dD_rBQSs2o/TLkJ1yxVviI/AAAAAAAAA3s/8iJV44hz8_w/image_thumb%5B12%5D.png?imgmax=800" width="399" height="185" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Oh, if you’re wondering, that colour scheme is the “&lt;a href="http://studiostyl.es/schemes/easy-on-the-eyes-1" target="_blank"&gt;Easy on the Eyes&lt;/a&gt;” theme from &lt;a href="http://studiostyl.es/" target="_blank"&gt;StudioStyles&lt;/a&gt;, but with a light coloured background instead.&lt;/p&gt;  &lt;p&gt;As a note I’m also using DejaVu for my command prompt font as well:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_5dD_rBQSs2o/TLkJ2siH9uI/AAAAAAAAA3w/YQljcyFR3hg/s1600-h/image%5B14%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: ; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_5dD_rBQSs2o/TLkJ3FhBZ8I/AAAAAAAAA30/cBEjH_BiV_c/image_thumb%5B10%5D.png?imgmax=800" width="498" height="102" /&gt;&lt;/a&gt;&lt;/p&gt;    &lt;p&gt;Note that there’s a &lt;a href="http://github.com/andreberg/Meslo-Font/wiki/Using-Meslo-LG-with-the-Windows-Console" target="_blank"&gt;trick to getting other fonts in the command prompt&lt;/a&gt; these days, which I only ran across recently when looking at the &lt;a href="http://github.com/andreberg/Meslo-Font" target="_blank"&gt;Meslo font&lt;/a&gt;, which for reference looks like this in Visual Studio:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_5dD_rBQSs2o/TLkJ4akR2II/AAAAAAAAA34/ERw3IZMs20s/s1600-h/image%5B22%5D.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; margin: ; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_5dD_rBQSs2o/TLkJ43xRCpI/AAAAAAAAA38/NkyXDxZJTxQ/image_thumb%5B14%5D.png?imgmax=800" width="404" height="208" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;As you can see, Meslo is similar to DejaVu in overall style but the characters are a tad wider, punctuation characters are bolded and the line spacing is a little larger as well.&amp;#160; I’m happy to stick with DejaVu.&lt;/p&gt;  &lt;p&gt;What about you? Are you still using the defaults or have you given your settings that personal touch? Feel free to post a comment with a link to your font of choice.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-4175859893519632140?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/4175859893519632140/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2010/10/visual-studio-and-deja-vu.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/4175859893519632140'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/4175859893519632140'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2010/10/visual-studio-and-deja-vu.html' title='Visual Studio and Deja Vu'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/_5dD_rBQSs2o/TLkJ0ewXYHI/AAAAAAAAA3k/eWr4qFJn6Ao/s72-c/image_thumb%5B8%5D.png?imgmax=800' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-5596597832990321133</id><published>2010-09-20T21:54:00.001+10:00</published><updated>2010-09-20T21:54:25.640+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='general'/><title type='text'>Is The Way Hiring Happens Changing?</title><content type='html'>&lt;p&gt;For those who don’t know (or just plain forgot) I run a weekly podcast for developers at &lt;a href="http://www.talkingshopdownunder.com"&gt;http://www.talkingshopdownunder.com&lt;/a&gt; and in &lt;a href="http://www.talkingshopdownunder.com/2010/08/episode-24-developer-experience-index.html" target="_blank"&gt;one of the episodes&lt;/a&gt; I talked with Matthew Wills about an idea I blogged about – the &lt;a href="http://www.richard-banks.org/2010/08/developer-experience-index.html" target="_blank"&gt;Developer Experience Index&lt;/a&gt; and wether we need one or not to improve the way we find and hire talent and raise the general skill level of the industry.&lt;/p&gt;  &lt;p&gt;Interestingly I just ran across an opinion piece on GigaOm about the way hiring is changing and wether resumes are headed the way of the dodo - &lt;a title="http://gigaom.com/collaboration/the-future-of-work-wont-contain-resumes/" href="http://gigaom.com/collaboration/the-future-of-work-wont-contain-resumes/"&gt;http://gigaom.com/collaboration/the-future-of-work-wont-contain-resumes/&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;It’s worth having a read, and most interestingly talks about the social networking aspects of hiring and how LinkedIn and your online presence in open source efforts can be a reflection of how well respected you are and wether you’re a good potential hire or not.&lt;/p&gt;  &lt;p&gt;I still don’t think we’re there yet, but the aggregation of social networking information into a ranking of some kind is a very interesting approach to finding people, though likely will tend to favour those with the best connections or most popular projects, not necessarily the best developers for your team.&amp;#160; Still, if nothing else, it reflects passion and involvement, and without passion for what they do a developer is pretty much worthless.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-5596597832990321133?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/5596597832990321133/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2010/09/is-way-hiring-happens-changing.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/5596597832990321133'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/5596597832990321133'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2010/09/is-way-hiring-happens-changing.html' title='Is The Way Hiring Happens Changing?'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-1258876848699614367</id><published>2010-09-15T13:50:00.001+10:00</published><updated>2010-09-15T14:02:12.571+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='TFS'/><title type='text'>How To Fix Branches After Migration to TFS2010</title><content type='html'>&lt;p&gt;Let’s say you’ve just migrated your code from another version control system to TFS2010 by grabbing the old code, copying it into your workspace as is, and then checking it in to TFS as a single massive import.&amp;#160; It’s a really easy and common way to do tip migration.&lt;/p&gt;  &lt;p&gt;When you do this, you will notice that all your branch relationships are missing and source control will look something like this:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_5dD_rBQSs2o/TJA6Ot2xkUI/AAAAAAAAA2g/ENW4OZHrqwA/s1600-h/image%5B47%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="image" border="0" alt="image" src="http://lh3.ggpht.com/_5dD_rBQSs2o/TJA6PcDVGEI/AAAAAAAAA2k/Dk3B82Kb3FM/image_thumb%5B19%5D.png?imgmax=800" width="217" height="117" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;The old system had Trunk branched to Feature X and Version 1, etc, but now there’s no relationships and trying to merge from Trunk to a branch brings up a dialog with no targets.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_5dD_rBQSs2o/TJA6RLc0H6I/AAAAAAAAA2o/wIBaSV76Dzo/s1600-h/image%5B46%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="image" border="0" alt="image" src="http://lh6.ggpht.com/_5dD_rBQSs2o/TJA6SZyw_cI/AAAAAAAAA2s/jXvx5GQ3M6k/image_thumb%5B18%5D.png?imgmax=800" width="480" height="402" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Bummer, huh?&amp;#160; Yeah, but what did you expect would happen? You’ve just imported a bunch of folders from the file system and TFS has no way of knowing that they’re branches.&lt;/p&gt;  &lt;p&gt;We need to let it know which folders the branches are and the relationships between them.&amp;#160; Here’s how we do it:&lt;/p&gt;  &lt;h3&gt;Step 1: Convert Folders to Branches&lt;/h3&gt;  &lt;p&gt;Right click the trunk folder (or whatever your root branch folder is) and convert it to a branch.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_5dD_rBQSs2o/TJA6S8XQiFI/AAAAAAAAA2w/o3C5eLiibyE/s1600-h/image%5B44%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="image" border="0" alt="image" src="http://lh3.ggpht.com/_5dD_rBQSs2o/TJA6Tmo02FI/AAAAAAAAA20/nnvOc3KNKfA/image_thumb%5B16%5D.png?imgmax=800" width="474" height="101" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;It should now look like this:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_5dD_rBQSs2o/TJA6Ucq4U3I/AAAAAAAAA24/Af4dNlkax-M/s1600-h/image%5B11%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="image" border="0" alt="image" src="http://lh4.ggpht.com/_5dD_rBQSs2o/TJA6VYEqDBI/AAAAAAAAA28/2-SfTEQUyjg/image_thumb%5B3%5D.png?imgmax=800" width="207" height="113" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Do this for all the other target branches you have.&amp;#160; e.g. Feature X and Version 1.&lt;/p&gt;  &lt;h3&gt;Step 2: Perform a Baseless Merge&lt;/h3&gt;  &lt;p&gt;Next, perform a TFS baseless merge to establish a relationship between the parent branch and the child branches.&amp;#160; Don’t worry about the contents, we’re only going to merge the folder, not it’s contents.&lt;/p&gt;  &lt;p&gt;TFS baseless merges are performed via the command line, so open up your VS2010 command prompt and do the following:&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:781501e9-6816-4112-bb41-50b2e0ba7ae8" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: xml;"&gt;cd &amp;lt;workspaceFolder&amp;gt;&lt;br /&gt;tf merge /baseless &amp;lt;trunk&amp;gt; &amp;lt;childBranch&amp;gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Check in your pending changes.&lt;/p&gt;&lt;h3&gt;Step 3 Reparent Child Branches&lt;/h3&gt;&lt;p&gt;At this point we have a loose link between the branches but the relationships are still not finalised.&amp;#160; We need to fix that.&lt;/p&gt;&lt;p&gt;Select your &lt;em&gt;child &lt;/em&gt;branch in Source Control Explorer and from the File menu choose Source Control –&amp;gt; Branching and Merging –&amp;gt; Reparent...&amp;#160; NOTE: This is not available via the context menu in Source Control Explorer.&lt;/p&gt;&lt;p&gt;&lt;a href="http://lh4.ggpht.com/_5dD_rBQSs2o/TJA6WNnf5AI/AAAAAAAAA3A/c4mMKA2A0c8/s1600-h/image%5B49%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="image" border="0" alt="image" src="http://lh5.ggpht.com/_5dD_rBQSs2o/TJA6XWXrhII/AAAAAAAAA3E/PJixueE_XU4/image_thumb%5B21%5D.png?imgmax=800" width="460" height="139" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;In the dialog box, choose the appropriate branch as the new parent.&amp;#160; There generally should only be one, unless you have done multiple baseless merges.&lt;/p&gt;&lt;p&gt;&lt;a href="http://lh3.ggpht.com/_5dD_rBQSs2o/TJA6X5JXlTI/AAAAAAAAA3I/16_4Jmf-HOE/s1600-h/image%5B51%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="image" border="0" alt="image" src="http://lh6.ggpht.com/_5dD_rBQSs2o/TJA6YrK7SDI/AAAAAAAAA3M/szH_AbkBtHM/image_thumb%5B23%5D.png?imgmax=800" width="406" height="400" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;Now look at the properties of your child branch and you should see something like this:&lt;/p&gt;&lt;p&gt;&lt;a href="http://lh6.ggpht.com/_5dD_rBQSs2o/TJA6ZRrYPzI/AAAAAAAAA3Q/UUt341JsPzQ/s1600-h/image%5B52%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="image" border="0" alt="image" src="http://lh6.ggpht.com/_5dD_rBQSs2o/TJA6auv_q9I/AAAAAAAAA3U/zguQjadgz8I/image_thumb%5B24%5D.png?imgmax=800" width="348" height="177" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;Excellent!&amp;#160; If you go to your Trunk branch and in the branch context menu choose View Hierarchy. You should see something like this:&lt;/p&gt;&lt;p&gt;&lt;a href="http://lh5.ggpht.com/_5dD_rBQSs2o/TJA6bHfIy7I/AAAAAAAAA3Y/7mOXD-tbEOU/s1600-h/image%5B54%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="image" border="0" alt="image" src="http://lh3.ggpht.com/_5dD_rBQSs2o/TJA6b8pP8JI/AAAAAAAAA3c/_Gu-BwvnuZE/image_thumb%5B26%5D.png?imgmax=800" width="332" height="175" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;Brilliant.&amp;#160; Now we’re all done.&amp;#160; You can carry on using TFS as you normally would.&lt;/p&gt;&lt;p&gt;Enjoy!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-1258876848699614367?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/1258876848699614367/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2010/09/how-to-fix-branches-after-migration-to.html#comment-form' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/1258876848699614367'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/1258876848699614367'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2010/09/how-to-fix-branches-after-migration-to.html' title='How To Fix Branches After Migration to TFS2010'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/_5dD_rBQSs2o/TJA6PcDVGEI/AAAAAAAAA2k/Dk3B82Kb3FM/s72-c/image_thumb%5B19%5D.png?imgmax=800' height='72' width='72'/><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-6713761090522622182</id><published>2010-09-13T23:04:00.001+10:00</published><updated>2010-09-13T23:04:50.411+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='general'/><title type='text'>Aw, Snap! (or How I got Hacked)</title><content type='html'>&lt;p&gt;So there I am surfing the web and catching up on some blogs and news when I notice my Skype icon blinking away at me in the windows taskbar.&amp;#160; Hmm, someone’s sent me an IM, thinks I only to discover that Skype logged me out.&lt;/p&gt;  &lt;p&gt;That’s weird I say to myself.&amp;#160; I guess I’ll just log in again. Click.&amp;#160; What? Invalid login?&amp;#160; Hmm.&amp;#160; Try again. Click! Click!! Click!!!&amp;#160; Dammit! What’s going on. Oh look – I just got an email.&amp;#160; It’s from Skype.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_5dD_rBQSs2o/TI4hbtxirBI/AAAAAAAAA2Y/_bglUEkL25E/s1600-h/image%5B4%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_5dD_rBQSs2o/TI4hca-R4qI/AAAAAAAAA2c/bi3_MY5Aa2k/image_thumb%5B2%5D.png?imgmax=800" width="500" height="166" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Aw, Snap!!!&amp;#160; (or insert expletive of your choice).&lt;/p&gt;  &lt;p&gt;Yes, my Skype account had been hacked and on gaining entry the attackers had immediately changed the account password and email address on me, blocking me out.&lt;/p&gt;  &lt;p&gt;Long story short, the good news is that after immediately contacting Skype they restored my account to me (thank you!!) in less than 2.5 hours and it only cost me a few euro in Skype credits and a few hours of panic.&amp;#160; Yet the whole thing was entirely preventable.&lt;/p&gt;  &lt;h3&gt;But How?&lt;/h3&gt;  &lt;p&gt;So the obvious question: how did it happen? Simple – I was stupid and lazy and forgot some of the basics of online security.&amp;#160; Doh!&lt;/p&gt;  &lt;p&gt;You see, when I’d first used Skype years and years ago, it was way back in it’s early days and at that time I used an insecure password.&amp;#160; It was a dictionary word of all things – I know! I know! You don’t have to tell me! But because I was just trying the thing out I figured that if it was useful then I’d go back and change my password later on.&amp;#160; At the time I’d also clicked the auto-login option and let Skype sign itself in for me.&lt;/p&gt;  &lt;p&gt;Unfortunately, because I never used it much early on I never went back and changed the password, and over time forgot what it was.&amp;#160; Kind of like when we tell ourselves we’re going to write those unit tests once we get this code working.&amp;#160; The reality is that we get distracted or do something else, forgetting to go back and clean up after ourselves. As the saying goes, the road to hell is paved with good intentions.&lt;/p&gt;  &lt;p&gt;Of course, at any point I could’ve done the “I forgot my password” routine, but as Skype kept logging itself in, I just forgot about it and fixing the password drifted to the bottom of my “stuff I should really do to be a good human being but that I’ll likely never get around to doing because I’d rather be sleeping or eating or having s..” well, cough, ahem, you get the idea. &lt;/p&gt;  &lt;p&gt;Using a real word for a password all those years ago and not taking the one measly minute needed to fix it is pretty lazy of me, huh? Yet so many people do it.&amp;#160; And it could’ve turned out so much worse.&amp;#160; Thankfully that password wasn’t used anywhere else, so I didn’t have to worry about the hackers using that to break into other accounts of mine.&lt;/p&gt;  &lt;p&gt;Of course, what irks me most is that these days I use KeePass and I’ve even &lt;a title="How I learnt to love a password manager" href="http://www.richard-banks.org/2009/09/how-i-learnt-to-love-password-manager.html" target="_blank"&gt;blogged about it in the past&lt;/a&gt; (though I now use DropBox instead of Mesh for syncing), and I’ve read plenty of tales in the past of accounts being hacked and how easy dictionary based attacks are to pull off.&amp;#160; And yet here I am.&amp;#160; A victim.&amp;#160; Grrr. Dumb, Richard. Dumb!&lt;/p&gt;  &lt;p&gt;So what are my lessons?&amp;#160; What do I learn from my dumbicity?&lt;/p&gt;  &lt;p&gt;1. NEVER.&amp;#160; Not ever.&amp;#160; Not even on my deathbed will I use a weak password for anything.&amp;#160; Even if it’s a throw away account to register for some software trial that I’ll never use again.&amp;#160; I have Keepass and it can generate&amp;#160; passwords for me that are safe.&lt;/p&gt;  &lt;p&gt;2. Never reuse passwords across sites.&amp;#160; Once a password is compromised on one site it’s compromised everywhere.&lt;/p&gt;  &lt;p&gt;3. Keep my OpenId accounts extra secure.&amp;#160; If I lose an OpenId account I’d be really messed up, especially as these days the use of OpenId is increasing and more and more sites are accessed only via OpenId accounts.&lt;/p&gt;  &lt;p&gt;4. Go and change all those other passwords I have just in case.&amp;#160; I can’t think of how many other sites I’ve used over the years that I no longer visit and where I may have old passwords laying around.&amp;#160; If one of those sites was compromised and I’ve reused that password elsewhere and not realised it, what else can the attackers get to?&lt;/p&gt;  &lt;p&gt;So, enough beating myself up about this. I’ve learned my lesson.&amp;#160; Hopefully you can learn from my mistake as well and avoid being a statistic like me.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-6713761090522622182?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/6713761090522622182/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2010/09/aw-snap-or-how-i-got-hacked.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/6713761090522622182'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/6713761090522622182'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2010/09/aw-snap-or-how-i-got-hacked.html' title='Aw, Snap! (or How I got Hacked)'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/_5dD_rBQSs2o/TI4hca-R4qI/AAAAAAAAA2c/bi3_MY5Aa2k/s72-c/image_thumb%5B2%5D.png?imgmax=800' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-3512419434720552945</id><published>2010-08-31T15:16:00.002+10:00</published><updated>2010-08-31T16:42:09.927+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tech.ed'/><title type='text'>TechEd Australia 2010</title><content type='html'>&lt;p&gt;I’ve been quiet the last few weeks due to TechEd Australia coming up and my need to actually put together a session that flowed nicely, as well as getting through all the usual client work and so forth that was needed.&amp;#160; Well, actually I was quiet on the blogging front, my &lt;a title="Richard&amp;#39;s Twitter Account" href="http://twitter.com/rbanks54" target="_blank"&gt;tweeting&lt;/a&gt; continued as per usual (140 characters doesn’t take much time to bash out).&lt;/p&gt;  &lt;p&gt;Yet again, TechEd Australia was a great personal experience for me.&amp;#160; I delivered my unit testing session and got some great feedback (thanks to everyone who attended and put in an eval!), I also managed to record 5 episodes for the &lt;a title="Talking Shop Down Under podcast" href="http://www.talkingshopdownunder.com" target="_blank"&gt;Talking Shop Down Under podcast&lt;/a&gt;, did plenty of networking, including meeting the &lt;a title="Dot Net Rocks podcast" href="http://www.dotnetrocks.com/" target="_blank"&gt;DotNetRocks&lt;/a&gt; guys as well as a bunch of other people I knew only via twitter, and I even attended some sessions and learnt some stuff.&lt;/p&gt;  &lt;p&gt;For those interested, Microsoft should soon make my session available online and I’ll update this with the location once it goes live.&lt;/p&gt;  &lt;p&gt;Update: The recording will be up at: &lt;a href="http://www.msteched.com/2010/Australia/DEV362"&gt;http://www.msteched.com/2010/Australia/DEV362&lt;/a&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-3512419434720552945?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/3512419434720552945/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2010/08/teched-australia-2010.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/3512419434720552945'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/3512419434720552945'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2010/08/teched-australia-2010.html' title='TechEd Australia 2010'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-7485167745804578878</id><published>2010-08-09T22:18:00.002+10:00</published><updated>2010-08-09T22:24:15.780+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='community'/><title type='text'>The Developer Experience Index</title><content type='html'>&lt;p&gt;I’ve been musing on developer skills, certification rorts, hiring, and a whole lot more recently and I’ve been thinking that it’s about time we had something akin to the Windows Experience Index, but for developers – what I’ve whimsically titled the Developer Experience Index.&amp;#160; A representation of a persons skills and abilities in various areas of development and maybe some way of rating them overall as a developer.&lt;/p&gt;  &lt;p&gt;Because my thinking on this is still solidifying, instead of writing down all my thoughts and having a long rambling post that gets misunderstood, I’d instead love for you to have a listen to &lt;a title="Talking Shop Down Under Episode 24 - The Developer Experience Index" href="http://www.talkingshopdownunder.com/2010/08/episode-24-developer-experience-index.html" target="_blank"&gt;Episode 24 of Talking Shop Down Under&lt;/a&gt; and get a feel for what I’m really talking about. It’s only 24 minutes so it won’t take forever to listen to.&lt;/p&gt;  &lt;p&gt;I’m after feedback and thoughts on this to decide if it’s worth pursuing, so even if you don’t subscribe to my awesome podcast (and why not!?), &lt;a title="Talking Shop Down Under Episode 24 - The Developer Experience Index" href="http://www.talkingshopdownunder.com/2010/08/episode-24-developer-experience-index.html" target="_blank"&gt;have a listen to this episode&lt;/a&gt; and see what you think.&amp;#160; Even better, spread the word about it so that others can talk about it as well and we can get some conversation happening.&lt;/p&gt;  &lt;p&gt;Would you be willing to contribute to such a community? Would you be willing to go through a community certification process?&amp;#160; If you are in a position to hire people, would you rather interview people who are rated well by their peers or those who are certified by an organisation that makes money from people attempting certification exams? Am I completely bonkers and this is a really dumb idea?  Let me know&lt;/p&gt;  &lt;p&gt;P.S. For those wondering (and who haven’t yet listened to the episode) I am aware of the &lt;a title="Black Belt Factory" href="http://www.blackbeltfactory.com/" target="_blank"&gt;Java Black Belt&lt;/a&gt; exams, and while that’s a good approach and shows the potential of this, it’s not quite what I’m after.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-7485167745804578878?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/7485167745804578878/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2010/08/developer-experience-index.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/7485167745804578878'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/7485167745804578878'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2010/08/developer-experience-index.html' title='The Developer Experience Index'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-6778493154019417437</id><published>2010-08-08T22:53:00.001+10:00</published><updated>2010-08-08T22:53:00.232+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='TFS'/><title type='text'>How to Build VB6 Apps with TFS Team Build 2010</title><content type='html'>&lt;p&gt;So you’ve got yourself a nice, shiny, new TFS 2010 server and you’re using its build automation features to build your .NET code but you also have some, shall we say it, “legacy” VB6 code laying around that you have to keep alive.&amp;#160; You’ve retired source safe and installed &lt;a title="Get VB6 source control connected to TFS 2010" href="http://visualstudiogallery.msdn.microsoft.com/en-us/bce06506-be38-47a1-9f29-d3937d3d88d6" target="_blank"&gt;the MSSCCI provider TFS 2010&lt;/a&gt; so TFS is your source repository but now you want the build server to build your VB6 code just like it does for your .NET code.&lt;/p&gt;  &lt;p&gt;Here’s how to get VB6 applications built using TFS 2010:&lt;/p&gt;  &lt;h3&gt;Preliminary Steps&lt;/h3&gt;  &lt;p&gt;1. Go to your build templates folder in source control and make a copy of DefaultTemplate.xaml.&amp;#160; Call it VB6BuildTemplate or something similarly memorable.&lt;/p&gt;  &lt;p&gt;2. Open up the new template and find the “Compile the Project” activity sequence.&amp;#160; For reference it’s roughly located in “Compile, Test and Associate Changesets and Work Items” &amp;gt; “Compile and Test” &amp;gt; “Compile and Test for Configuration” &amp;gt; “If BuildSettings.HasProjectsToBuild” &amp;gt; “Compile the Project”&lt;/p&gt;  &lt;p&gt;3. Find the “Run MSBuild for Project” activity and delete it.&amp;#160; We’re going to replace that with our own tasks.&lt;/p&gt;  &lt;h3&gt;Ensure the Output Directory Exists&lt;/h3&gt;  &lt;p&gt;When you that MSBuild activity runs to compile your .NET projects the output locations are created for you automatically but since we’re not using MSBuild we don’t have that luxury for our VB6 compilations so we have to create the folder ourselves.&lt;/p&gt;  &lt;p&gt;Start by dragging an “If” Activity into the sequence&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_5dD_rBQSs2o/TFq0L8-fw7I/AAAAAAAAA1E/DJIHplVIV-4/s1600-h/image4.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_5dD_rBQSs2o/TFq0MgB0riI/AAAAAAAAA1I/SS1aBNFQr88/image_thumb2.png?imgmax=800" width="433" height="262" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Then drag a CreateDirectory task (from Team Foundation Build Activities) into the “Then” section as shown&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_5dD_rBQSs2o/TFq0NN7w0ZI/AAAAAAAAA1M/a9L_chRSLpo/s1600-h/image10.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_5dD_rBQSs2o/TFq0NlWLr1I/AAAAAAAAA1Q/bg4Uzq7g7Fw/image_thumb6.png?imgmax=800" width="490" height="318" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Now the point of this is to check if the outputDirectory has already been created and to create it if it hasn’t.&lt;/p&gt;  &lt;p&gt;Set the Condition for the If Activity to: Not System.IO.Directory.Exists(outputDirectory).&lt;/p&gt;  &lt;p&gt;Set the Diirectory property of the createDirectory to outputDirectory&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_5dD_rBQSs2o/TFq0OQOb47I/AAAAAAAAA1U/3iBZ_fH4PdQ/s1600-h/image15.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_5dD_rBQSs2o/TFq0PPtcx2I/AAAAAAAAA1Y/qADtiVxyyWQ/image_thumb9.png?imgmax=800" width="328" height="153" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;For maintainability, rename the activities in the designer as well so that you can better understand what is happening.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_5dD_rBQSs2o/TFq0PvJjQgI/AAAAAAAAA1c/wrL6nvmovig/s1600-h/image20.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_5dD_rBQSs2o/TFq0QZ3NFrI/AAAAAAAAA1g/yIE7HHwHTe4/image_thumb12.png?imgmax=800" width="479" height="222" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;By the way, if you are sure the directory doesn’t get created earlier in your process then you can drop the if activity and just place the createDirectory activity into the flow directly. &lt;/p&gt;  &lt;h3&gt;Ensure a Log Directory Exists&lt;/h3&gt;  &lt;p&gt;When we run the VB6 compiler in a build we need to capture the log output to a text file otherwise VB will want to try and open a dialog box telling us there was a compiler problem and want us to click OK. Unfortunately when running unattended, there is no UI and we will never see a window to click the link on.&lt;/p&gt;  &lt;p&gt;We’re going to place our log file output in the drop location so that we can easily access it once the build is complete.&lt;/p&gt;  &lt;p&gt;Create another if task (or copy/paste) from the previous step and ensure that we use logFileDropLocation instead of outputDirectory.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_5dD_rBQSs2o/TFq0RKUPgII/AAAAAAAAA1k/LgdwNIyGg9I/s1600-h/image25.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_5dD_rBQSs2o/TFq0RvwFleI/AAAAAAAAA1o/MI5XaDZ8XzI/image_thumb15.png?imgmax=800" width="475" height="227" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;h3&gt;Call the VB6 Compiler&lt;/h3&gt;  &lt;p&gt;Now drag an InvokeProcess activity into the workflow and set it up as follows:&lt;/p&gt;  &lt;table border="0" cellspacing="0" cellpadding="2" width="553"&gt;&lt;tbody&gt;     &lt;tr&gt;       &lt;td valign="top" width="99"&gt;DisplayName&lt;/td&gt;        &lt;td valign="top" width="452"&gt;VB6 Compiler&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="99"&gt;FileName&lt;/td&gt;        &lt;td valign="top" width="452"&gt;Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86) + &amp;quot;\Microsoft Visual Studio\VB98\VB6.exe&amp;quot;&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="99"&gt;Arguments&lt;/td&gt;        &lt;td valign="top" width="452"&gt;&amp;quot;/make &amp;quot;&amp;quot;&amp;quot; + localProject + &amp;quot;&amp;quot;&amp;quot; /out &amp;quot;&amp;quot;&amp;quot; + logFileDropLocation + &amp;quot;\vb6.log&amp;quot;&amp;quot; /outdir &amp;quot;&amp;quot;&amp;quot; + outputDirectory + &amp;quot;&amp;quot;&amp;quot;&amp;quot;&lt;/td&gt;     &lt;/tr&gt;      &lt;tr&gt;       &lt;td valign="top" width="99"&gt;Result&lt;/td&gt;        &lt;td valign="top" width="452"&gt;VB6Result&lt;/td&gt;     &lt;/tr&gt;   &lt;/tbody&gt;&lt;/table&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;Note that the VB6Result variable will likely to show up with a warning until you create it as a variable in your workflow. So on the workflow create the variable with a type of Int32 and a scope of “Compile the Project”&lt;/p&gt;  &lt;p&gt;If you wish, you can also send stdOutput and errOutput from the InvokeProcess tasks to the build log using the WriteBuildMessage and WriteBuildWarning tasks.&amp;#160; For each of these use the appropriate std/errOutput variables as the values for the Message property, though with the VB6 compiler you’re unlikely to see any messages since we’re sending output to the log file.&lt;/p&gt;  &lt;p&gt;The workflow should now look something like this:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_5dD_rBQSs2o/TFq0SFCN4BI/AAAAAAAAA1s/5MAixCFarU0/s1600-h/image31.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_5dD_rBQSs2o/TFq0S1ktZwI/AAAAAAAAA1w/NaPBuoY023E/image_thumb19.png?imgmax=800" width="253" height="504" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;h3&gt;Check the Compiler Result&lt;/h3&gt;  &lt;p&gt;Finally we need to check if the compile passed or not.&amp;#160; If it fails we will get a non-zero result back from InvokeProcess &lt;/p&gt;  &lt;p&gt;If we don’t check this then the build process just assumes things work and continues on it’s merry way. so add another “If” Activity to the flow and in the Then part add a “Throw” Activity from the Error Handling group.&amp;#160; Set the Throw Activity’s Exception property to “New Exception” and the Condition on the “If” activity to “VB6Result &amp;lt;&amp;gt; 0”.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_5dD_rBQSs2o/TFq0UDcKjuI/AAAAAAAAA10/aieXcCaP_d4/s1600-h/image36.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_5dD_rBQSs2o/TFq0UmOQR1I/AAAAAAAAA14/GQ9MErSIBTM/image_thumb22.png?imgmax=800" width="480" height="215" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Don’t forget to rename the activities to help you diagnose any problems.&lt;/p&gt;  &lt;h3&gt;Use It in a Build&lt;/h3&gt;  &lt;p&gt;Finally, save your workflow, check it in to source control (in the BuildProcessTemplates folder) and then create a build definition using the new template.&lt;/p&gt;  &lt;p&gt;Trigger the build and check the compilation results.&amp;#160; Everything should now work as expected.&amp;#160; Now all you need to do it retire that VB6 code :-)&amp;#160; Good luck with that!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-6778493154019417437?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/6778493154019417437/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2010/08/how-to-build-vb6-apps-with-tfs-team.html#comment-form' title='11 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/6778493154019417437'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/6778493154019417437'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2010/08/how-to-build-vb6-apps-with-tfs-team.html' title='How to Build VB6 Apps with TFS Team Build 2010'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/_5dD_rBQSs2o/TFq0MgB0riI/AAAAAAAAA1I/SS1aBNFQr88/s72-c/image_thumb2.png?imgmax=800' height='72' width='72'/><thr:total>11</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-1381973638938305662</id><published>2010-08-06T08:35:00.001+10:00</published><updated>2010-08-06T08:57:46.073+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mocking'/><title type='text'>Mocking Comparison – Part 12: The UnMockables</title><content type='html'>&lt;p&gt;The frameworks we’ve been looking at in previous parts only work well when they can override the properties of the classes you wish to mock, or when you use interfaces. So what happens when you need to deal with a test that is time dependant, or if you want to verify data being written to the console or you have to deal with SharePoint or other similar products that are a dogs vomit of static classes, sealed types and leaky abstractions that make testing with them almost impossible using normal techniques?&lt;/p&gt;  &lt;p&gt;The answer is to move away from regular mocking frameworks and use tools like &lt;a href="http://site.typemock.com/typemock-isolator-product" target="_blank"&gt;TypeMock Isolator&lt;/a&gt;, &lt;a href="http://research.microsoft.com/en-us/projects/moles/" target="_blank"&gt;Microsoft Moles&lt;/a&gt; or &lt;a href="http://www.telerik.com/products/mocking.aspx" target="_blank"&gt;Telerik Just Mock&lt;/a&gt; (as a note, I don’t have a copy of JustMock so I’m going to leave it out of this series)&lt;/p&gt;  &lt;h3&gt;DateTime.Now&lt;/h3&gt;  &lt;p&gt;So let’s say you’ve got a class that references DateTime.Now and changes behaviour based on the time of day. How do you test behaviour correctly and more importantly,how do you do this reliably?&amp;#160; You can’t really mess with the system clock.&amp;#160; The trick is to intercept the call to DateTime.Now and provide your own method that gets called instead, and this is what TypeMock and Moles both allow you to do.&lt;/p&gt;  &lt;h3&gt;TypeMock&lt;/h3&gt;  &lt;p&gt;Here’s the basic code for mocking out DateTime.Now with TypeMock&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:e6d48133-5510-45b7-8d3e-f269571b5bfb" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: c#;"&gt;readonly DateTime dateToUse = new DateTime(2010, 07, 17, 8, 0, 0);&lt;br /&gt;&lt;br /&gt;[Fact]&lt;br /&gt;public void Isolate_current_time()&lt;br /&gt;{&lt;br /&gt; Isolate.WhenCalled(() =&amp;gt; DateTime.Now).WillReturn(dateToUse);&lt;br /&gt; Assert.Equal(dateToUse, DateTime.Now);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Pretty simple stuff.&lt;/p&gt;&lt;p&gt;If you wanted to provide a method implementation rather the just returning a value you would replace the .WillReturn() call with .DoInstead() &lt;/p&gt;&lt;p&gt;So let’s see what this looks like in a test.&amp;#160; Once again, this is somewhat testing the mock which you shouldn’t do in the real world, but it does show you what the syntax is like, which is the goal after all.&lt;/p&gt;&lt;p&gt;Oh, as a reminder the relevant code from the class under test is as follows:&lt;/p&gt;&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:263b9194-8005-49a1-8c59-4bc097e37c11" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: c#;"&gt;public void CleanMonkey()&lt;br /&gt;{&lt;br /&gt; // &amp;lt;snip!&amp;gt;&lt;br /&gt; if (AssignedMonkey.IsAwake(DateTime.Now))&lt;br /&gt;  AssignedMonkey.Clean();&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So in our test we are going to set the mock to only return true if the time passed to the IsAwake method matches our specific date time.&amp;#160; In other words, we should see that DateTime.Now is returning the value we specify, not the current time.&lt;/p&gt;&lt;p&gt;&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:f2891b08-c037-4272-ad38-05b5fd5b15d9" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: c#;"&gt;[Fact]&lt;br /&gt;public void Isolate_monkey_should_be_awake()&lt;br /&gt;{&lt;br /&gt; Isolate.WhenCalled(() =&amp;gt; DateTime.Now).WillReturn(dateToUse);&lt;br /&gt;&lt;br /&gt; var monkey = Substitute.For&amp;lt;IMonkey&amp;gt;();&lt;br /&gt; monkey.CurrentFleaCount().Returns(20);&lt;br /&gt; monkey.IsAwake(Arg.Is(dateToUse)).Returns(true);&lt;br /&gt;&lt;br /&gt; var keeper = new ZooKeeper();&lt;br /&gt; keeper.AssignedMonkey = monkey;&lt;br /&gt;&lt;br /&gt; keeper.CleanMonkey();&lt;br /&gt;&lt;br /&gt; monkey.Received().Clean();&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;/p&gt;&lt;p&gt;And as expected, this test works.&amp;#160; Now the keen eyed amongst you will see that I’m actually mixing TypeMock Isolator and NSubstitute in the one test.&amp;#160; I’m using Isolator to mock out the static DateTime.Now method and NSubstitute to create the mock monkey object.&lt;/p&gt;&lt;p&gt;Also, in terms of running, the tests there’s a few little changes to the tests and things take longer because Isolator is injecting itself in your code.&amp;#160; The other thing to note is that DateTime is part of the Base Class Library and TypeMock doesn’t support all of the methods in the BCL, just certain methods so if you want to mock out a method Isolator doesn’t support you’re on you own.&lt;/p&gt;&lt;h3&gt;Mircosoft Moles&lt;/h3&gt;&lt;p&gt;Moles is a framework that is usually obtained with the Pex download from Microsoft Research, but it’s also available in a standalone form.&lt;/p&gt;&lt;p&gt;Moles is still very much a product with some rough edges.&amp;#160; I’ve had moles randomly stop working with the only fix being an un/reinstall at times.&lt;/p&gt;&lt;p&gt;Now the good thing about Moles is that it can replace the method call for anything you want.&amp;#160; No limitations at all, however the way it does it is to inspect the assembly you want to mock and then it generates a separate buddy library that provides a way to mock out or stub the method calls you are interested in.&amp;#160; It’s fine when working with things like mscorlib or other assemblies that don’t really change, but if you’re applying it to your own libraries and they change method signatures regularly then you will need to regenerate the moles assemblies each time which is a pain.&lt;/p&gt;&lt;p&gt;The following code is using the Moles.Xunit extension to run the tests you can’t just run the tests via TestDriven.Net like you can with Isolator – you need to call the tests via the Moles TestRunner.&amp;#160; I use a batch file as follows to help with this:&lt;/p&gt;&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:6e24aa86-cf4c-442b-99b0-4c2f7bbc57a3" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: xml;"&gt;cd c:\MyCode\bin\debug&lt;br /&gt;"C:\Program Files (x86)\Microsoft Moles\bin\moles.runner.x86.exe" Monkeys.Moles.Tests.dll /runner:c:\path\xunit-1.5\xunit.console.x86.exe /x86 &lt;br /&gt;pause&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You’ll also need to update assemblyinfo.cs to include attributes that tells Moles which methods you are using like so:&lt;/p&gt;&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:6c78ca6f-c381-4740-a063-edbadfa66214" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: c#;"&gt;[assembly: MoledType(typeof(System.DateTime))]&lt;br /&gt;[assembly: MoledType(typeof(System.Console))]&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And once that’s in place finally you can write a test as follows:&lt;/p&gt;&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:d0ee6493-bc9a-442d-94a7-41aee835e4a4" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: c#;"&gt;[Fact]&lt;br /&gt;[Moled]&lt;br /&gt;public void Replace_current_time()&lt;br /&gt;{&lt;br /&gt; MDateTime.NowGet = () =&amp;gt; dateToUse;&lt;br /&gt; Assert.Equal(dateToUse, DateTime.Now);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;[Fact]&lt;br /&gt;[Moled]&lt;br /&gt;public void Monkey_should_be_awake_for_cleaning_at_eight_am()&lt;br /&gt;{&lt;br /&gt; MDateTime.NowGet = () =&amp;gt; dateToUse;&lt;br /&gt; var monkey = Substitute.For&amp;lt;IMonkey&amp;gt;();&lt;br /&gt; monkey.CurrentFleaCount().Returns(20);&lt;br /&gt; monkey.IsAwake(Arg.Is(dateToUse)).Returns(true);&lt;br /&gt;&lt;br /&gt; var keeper = new ZooKeeper();&lt;br /&gt; keeper.AssignedMonkey = monkey;&lt;br /&gt;&lt;br /&gt; keeper.CleanMonkey();&lt;br /&gt;&lt;br /&gt; monkey.Received().Clean();&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So here you may notice that we have an extra attribute on our test method to indicate that this test method uses Moles.&amp;#160; In addition we provide a lambda to MDateTime.NowGet instead of DateTime.Now.&amp;#160; This is the method in the buddy assembly that gets called when DateTime.Now is referenced.&amp;#160; Also, the method naming for Moled types gets a bit funny.&amp;#160; Methods are named based on the method you are mocking and then either a list of types based on the method being called or a Get/Set for properties.&amp;#160; It works, but it’s a little clunky.&lt;/p&gt;&lt;h3&gt;Conclusion&lt;/h3&gt;&lt;p&gt;So, my preference here from a syntax viewpoint is the Isolator syntax by far, though the fact that it can’t get to everything in the BCL (such as Console.WriteLine) and that it is a commercial product takes the shine off a little.&amp;#160; On the flip side I like the power of Moles but it’s very clunky to work with, breaks easily and is a pain to use when the target assembly changes a lot and the price and power doesn’t offset this problem.&lt;/p&gt;&lt;h3&gt;That’s All Folks&lt;/h3&gt;&lt;p&gt;And that, my friends, is that for this series.&lt;/p&gt;&lt;p&gt;I hope you’ve enjoyed it and have a good idea of what the various frameworks are capable of.&amp;#160; If you haven’t already guessed, my new favourite framework is &lt;a href="http://nsubstitute.github.com/" target="_blank"&gt;NSubstitute&lt;/a&gt;.&amp;#160; If you haven’t already, go give it a try and see what you think and give feedback to the guys that wrote it on the &lt;a href="http://groups.google.com/group/nsubstitute" target="_blank"&gt;NSubstitute mailing list&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Happy testing!&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Other posts in this series:&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-1-basics.html"&gt;1: The Basics&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-2-properties.html"&gt;2: Properties&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-3-interactions.html"&gt;3: Interactions&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-4-parameter.html"&gt;4: Parameter Constraints&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-5-repetitions.html"&gt;5: Repetitions&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-6-multiple.html"&gt;6: Multiple Calls&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-7-exceptions.html"&gt;7: Exceptions&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-8-recursive.html"&gt;8: Recursive Mocks&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-9-functions.html"&gt;9: Functions&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/08/mocking-comparison-part-10-events.html"&gt;10: Events&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/08/mocking-comparison-part-11-multiple.html"&gt;11: Multiple Interfaces&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-1381973638938305662?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/1381973638938305662/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2010/08/mocking-comparison-part-12-unmockables.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/1381973638938305662'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/1381973638938305662'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2010/08/mocking-comparison-part-12-unmockables.html' title='Mocking Comparison – Part 12: The UnMockables'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-163983093262748889</id><published>2010-08-05T23:00:00.001+10:00</published><updated>2010-08-06T08:57:27.863+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mocking'/><title type='text'>Mocking Comparison – Part 11: Multiple Interfaces</title><content type='html'>&lt;p&gt;Continuing with our comparison of Rhino Mocks, Moq and NSubstitute we have a look at a little used feature in mocking being the ability to generate mocks that implement multiple interfaces.&lt;/p&gt;  &lt;p&gt;Why would you do this though? Well, that’s a good question.&amp;#160; Simple example would be when your class under test expects and object to implement interface X and also implement IDisposable.&amp;#160; It doesn’t happen often, but when it does it’s nice to know the facility is there.&lt;/p&gt;  &lt;p&gt;What you’ll see in all the examples is that the mock natively implements a main interface and that to do any interactions with the methods of the second interface requires casting of the mock to that interface.&lt;/p&gt;  &lt;p&gt;For the purposes of the code we’re going to pretend that the monkeys of our little zoo are self managing, can act as ZooKeepers and can thus look after themselves.&amp;#160; It’s silly, but it shows the syntax.&lt;/p&gt;  &lt;h3&gt;Rhino Mocks&lt;/h3&gt;  &lt;p&gt;The thing to note here is that we can’t use GenerateStub here.&amp;#160; We have to use GenerateMock, which then means we don’t get automatically backed properties, so we have to set them up ourselves as well.&lt;/p&gt;  &lt;p&gt;   &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:c69c6164-c6d6-4aad-a5b1-63097d94bb31" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: c#;"&gt;[Fact]&lt;br /&gt;public void Rhino_multiple_interfaces()&lt;br /&gt;{&lt;br /&gt; var monkey = MockRepository.GenerateMock&amp;lt;IMonkey, IZooKeeper&amp;gt;();&lt;br /&gt; monkey.Stub(m =&amp;gt; m.Name).PropertyBehavior();&lt;br /&gt; ((IZooKeeper)monkey).Stub(k=&amp;gt; k.AssignedMonkey).PropertyBehavior();&lt;br /&gt;&lt;br /&gt; monkey.Name = "Spike";&lt;br /&gt; ((IZooKeeper)monkey).AssignedMonkey = monkey;&lt;br /&gt;&lt;br /&gt; Assert.Equal("Spike", ((IZooKeeper)monkey).AssignedMonkey.Name);&lt;br /&gt;&lt;br /&gt; Assert.IsAssignableFrom&amp;lt;IMonkey&amp;gt;(monkey);&lt;br /&gt; Assert.IsAssignableFrom&amp;lt;IZooKeeper&amp;gt;(monkey);&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;/p&gt;&lt;p&gt;You can also see that we have to cast monkey to IZooKeeper every time we want to do something on the IZooKeeper interface.&amp;#160; Annoying, but that’s the way it goes.&lt;/p&gt;&lt;h3&gt;Moq&lt;/h3&gt;&lt;p&gt;The code here is a little different in that we create the mock the normal way, and then add a new interface to it after it’s already created using the .As&amp;lt;T&amp;gt;() method.&lt;/p&gt;&lt;p&gt;Also, when we set up the property behaviour on the IZooKeeper interface we have to go through some ugly casting and the use of Mock.Get() because of the way Moq separates the Mock and mocked object instances.&amp;#160; Blech.&lt;/p&gt;&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:c59eb0c1-abb3-407b-b465-3a3282053ed2" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: c#;"&gt;[Fact]&lt;br /&gt;public void Moq_multiple_interfaces()&lt;br /&gt;{&lt;br /&gt; var monkey = new Mock&amp;lt;IMonkey&amp;gt;();&lt;br /&gt; monkey.As&amp;lt;IZooKeeper&amp;gt;();&lt;br /&gt; monkey.SetupProperty(m =&amp;gt; m.Name);&lt;br /&gt; Mock.Get((IZooKeeper)monkey.Object).SetupProperty(k =&amp;gt; k.AssignedMonkey);&lt;br /&gt;&lt;br /&gt; monkey.Object.Name = "Spike";&lt;br /&gt; ((IZooKeeper)monkey.Object).AssignedMonkey = monkey.Object;&lt;br /&gt; &lt;br /&gt; Assert.Equal("Spike", ((IZooKeeper)monkey.Object).AssignedMonkey.Name);&lt;br /&gt;&lt;br /&gt; Assert.IsAssignableFrom&amp;lt;IMonkey&amp;gt;(monkey.Object);&lt;br /&gt; Assert.IsAssignableFrom&amp;lt;IZooKeeper&amp;gt;(monkey.Object);&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;h3&gt;NSubstitute&lt;/h3&gt;&lt;p&gt;This code is by far the smallest because we don’t need to set up the property behaviours.&amp;#160; It’s uses the same approach as Rhino to create the mock itself, and still has the issues of needing to cast to the IZooKeeper interface, but apart from that it’s once again nice and clean code.&lt;/p&gt;&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:d9e575a5-b719-4653-8795-bcca7c46aa8e" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: c#;"&gt;[Fact]&lt;br /&gt;public void Nsubstitute_multiple_interfaces()&lt;br /&gt;{&lt;br /&gt; var monkey = Substitute.For&amp;lt;IMonkey, IZooKeeper&amp;gt;();&lt;br /&gt;&lt;br /&gt; monkey.Name = "Spike";&lt;br /&gt; ((IZooKeeper)monkey).AssignedMonkey = monkey;&lt;br /&gt;&lt;br /&gt; Assert.Equal("Spike", ((IZooKeeper)monkey).AssignedMonkey.Name);&lt;br /&gt;&lt;br /&gt; Assert.IsAssignableFrom&amp;lt;IMonkey&amp;gt;(monkey);&lt;br /&gt; Assert.IsAssignableFrom&amp;lt;IZooKeeper&amp;gt;(monkey);&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&amp;#160;&lt;/p&gt;&lt;p&gt;The choice of syntax here is again, quite easy to make.&amp;#160; NSubstitute takes it out.&lt;/p&gt;&lt;p&gt;This also represents a conclusion to the posts focusing on Rhino Mocks, Moq and NSubstitute in the mocking comparison series.&amp;#160; I’m not quite done though as I want to show some usage scenarios where those frameworks don’t work and where tools like Microsoft Moles and TypeMock fit in, so stay tuned – we’re not quite done yet! :-)&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Other posts in this series:&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-1-basics.html"&gt;1: The Basics&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-2-properties.html"&gt;2: Properties&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-3-interactions.html"&gt;3: Interactions&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-4-parameter.html"&gt;4: Parameter Constraints&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-5-repetitions.html"&gt;5: Repetitions&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-6-multiple.html"&gt;6: Multiple Calls&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-7-exceptions.html"&gt;7: Exceptions&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-8-recursive.html"&gt;8: Recursive Mocks&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-9-functions.html"&gt;9: Functions&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/08/mocking-comparison-part-10-events.html"&gt;10: Events&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/08/mocking-comparison-part-12-unmockables.html"&gt;12: The UnMockables&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-163983093262748889?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/163983093262748889/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2010/08/mocking-comparison-part-11-multiple.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/163983093262748889'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/163983093262748889'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2010/08/mocking-comparison-part-11-multiple.html' title='Mocking Comparison – Part 11: Multiple Interfaces'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-4985608053408502176</id><published>2010-08-04T12:20:00.001+10:00</published><updated>2010-08-06T08:57:05.994+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mocking'/><title type='text'>Mocking Comparison – Part 10: Events</title><content type='html'>&lt;p&gt;So far in our comparison we’ve been looking at mock objects as if they were much like any other object, but what happens when we want our mocks to either raise or subscribe to events?&lt;/p&gt;  &lt;p&gt;If you’re testing how your class under test reacts when it receives an event, or want to know if it raises an event with correct values then you really need your mock framework to be able to support this.&lt;/p&gt;  &lt;h2&gt;Subscribing To Events&lt;/h2&gt;  &lt;p&gt;To get a mock object to subscribe to an event is pretty easy.&amp;#160; Just do it like you normally would, and then if you want to assert anything about how the event was raised, simply assert that the subscription method was called as expected.&lt;/p&gt;  &lt;h3&gt;Rhino Mocks&lt;/h3&gt;  &lt;p&gt;&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:158bcf0a-7359-4a9c-a1ee-2bbf4cade68e" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: c#;"&gt;[Fact]&lt;br /&gt;public void Rhino_event_subscriber()&lt;br /&gt;{&lt;br /&gt; var monkey = MockRepository.GenerateMock&amp;lt;IMonkey&amp;gt;();&lt;br /&gt; var keeper = new ZooKeeper();&lt;br /&gt; &lt;br /&gt; keeper.OnBananaReady += monkey.BananaReady;&lt;br /&gt; &lt;br /&gt; keeper.FeedMonkeys();&lt;br /&gt; monkey.AssertWasCalled(m =&amp;gt; m.BananaReady(&lt;br /&gt;  Arg&amp;lt;object&amp;gt;.Is.Equal(keeper)&lt;br /&gt;  ,Arg&amp;lt;BananaEventArgs&amp;gt;.Matches(b =&amp;gt; b.IsRipe&lt;br /&gt; )));&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As you can see, we subscribe to the event normally, with the event being raised by the FeedMonkeys() call.&lt;/p&gt;&lt;p&gt;We then check that the event was called correctly and that the IsRipe flag was set correctly.&lt;/p&gt;&lt;h3&gt;Moq&lt;/h3&gt;&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:dc494d48-859b-457c-b290-4eeea0445999" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: c#;"&gt;[Fact]&lt;br /&gt;public void Moq_event_subscriber()&lt;br /&gt;{&lt;br /&gt; var monkey = new Mock&amp;lt;IMonkey&amp;gt;();&lt;br /&gt; var keeper = new ZooKeeper();&lt;br /&gt;&lt;br /&gt; keeper.OnBananaReady += monkey.Object.BananaReady;&lt;br /&gt;&lt;br /&gt; keeper.FeedMonkeys();&lt;br /&gt; monkey.Verify(m =&amp;gt; m.BananaReady(keeper, &lt;br /&gt;  It.Is&amp;lt;BananaEventArgs&amp;gt;(b =&amp;gt; b.IsRipe)));&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The syntax is much the same as for Rhino Mocks apart from the fact that the .Object. syntax again makes things feel clunky.&amp;#160; On the positive side of things, the better constraint syntax in Moq makes the verify call far less noisy.&lt;/p&gt;&lt;h3&gt;NSubstitute&lt;/h3&gt;&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:b27df2ee-a8ca-4f46-80ad-b100f7132e77" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: c#;"&gt;[Fact]&lt;br /&gt;public void Nsubstitute_event_subscriber()&lt;br /&gt;{&lt;br /&gt; var monkey = Substitute.For&amp;lt;IMonkey&amp;gt;();&lt;br /&gt; var keeper = new ZooKeeper();&lt;br /&gt;&lt;br /&gt; keeper.OnBananaReady += monkey.BananaReady;&lt;br /&gt;&lt;br /&gt; keeper.FeedMonkeys();&lt;br /&gt; monkey.Received().BananaReady(keeper,&lt;br /&gt;   Arg.Is&amp;lt;BananaEventArgs&amp;gt;(e =&amp;gt; e.IsRipe));&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The NSubstitute version is looks much the same as the Moq syntax, just without the .Object. stuff.&lt;/p&gt;&lt;h2&gt;Raising Events&lt;/h2&gt;&lt;p&gt;Raising an event is a pretty simply process.&amp;#160; We simply get our class under test to subscribe to an event on our mock object and then ask our mock to raise an event.&lt;/p&gt;&lt;h3&gt;Rhino Mocks&lt;/h3&gt;&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:0893c40a-bb4c-46db-b59c-e063e8d877c4" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: c#;"&gt;[Fact]&lt;br /&gt;public void Rhino_raising_an_event()&lt;br /&gt;{&lt;br /&gt; var monkey = MockRepository.GenerateMock&amp;lt;IMonkey&amp;gt;();&lt;br /&gt; var tourist = new Tourist();&lt;br /&gt;&lt;br /&gt; tourist.SeeAMonkey(monkey);&lt;br /&gt;&lt;br /&gt; monkey.Raise(m =&amp;gt; m.Dance += null, monkey, new EventArgs());&lt;br /&gt; Assert.Equal(1, tourist.PhotosTaken);&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The line to pay attention to here is the second last one.&amp;#160; The monkey.Raise() call.&amp;#160; What you notice in here is that in order to raise an event we need to pass in an expression that registers an empty event listener.&amp;#160; This is so Rhino can pick up the signature of the event you wish to raise.&lt;/p&gt;&lt;p&gt;Once that’s done we just provide arguments for the sender and event args that we wish to use for the event.&lt;/p&gt;&lt;p&gt;It’s a little weird looking but it works.&lt;/p&gt;&lt;h3&gt;Moq&lt;/h3&gt;&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:ed387b2b-2634-48a7-ba82-3b66ed679134" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: c#;"&gt;[Fact]&lt;br /&gt;public void Moq_raising_an_event()&lt;br /&gt;{&lt;br /&gt; var monkey = new Mock&amp;lt;IMonkey&amp;gt;();&lt;br /&gt; var tourist = new Tourist();&lt;br /&gt;&lt;br /&gt; tourist.SeeAMonkey(monkey.Object);&lt;br /&gt;&lt;br /&gt; monkey.Raise(m =&amp;gt; m.Dance += null, new EventArgs());&lt;br /&gt; Assert.Equal(1, tourist.PhotosTaken);&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The Moq code is much the same as the Rhino code, with the only difference being that by default the sender is the object raising the event and we don’t need to specify it.&lt;/p&gt;&lt;h3&gt;NSubstitute&lt;/h3&gt;&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:a018ab10-3d18-4eff-9fae-4bf5ac9397a0" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: c#;"&gt;[Fact]&lt;br /&gt;public void Nsubstitute_raising_an_event()&lt;br /&gt;{&lt;br /&gt; var monkey = Substitute.For&amp;lt;IMonkey&amp;gt;();&lt;br /&gt; var tourist = new Tourist();&lt;br /&gt;&lt;br /&gt; tourist.SeeAMonkey(monkey);&lt;br /&gt;&lt;br /&gt; monkey.Dance += Raise.Event(new EventArgs());&lt;br /&gt; Assert.Equal(1, tourist.PhotosTaken);&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here you see the code is much simpler.&amp;#160; We still need to register a pseudo event listener when we want to raise the argument, but at the same time, it’s a little more obvious what’s going on.&lt;/p&gt;&lt;p&gt;All three frameworks suffer from needing to use event subscriptions to figure out what event to fire, but that’s a result of limitations in the way C# works rather than a poor design decisions.&amp;#160; With that in mind the verdict on which syntax to choose goes to NSubstitute.&amp;#160; Since event subscription is much the same across all 3 frameworks there’s not much to differentiate them, however event raising in NSubstitute is cleaner and more expressive than the syntax of both Rhino and Moq, making my choice rather easy.&amp;#160; &lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Other posts in this series:&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-1-basics.html"&gt;1: The Basics&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-2-properties.html"&gt;2: Properties&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-3-interactions.html"&gt;3: Interactions&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-4-parameter.html"&gt;4: Parameter Constraints&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-5-repetitions.html"&gt;5: Repetitions&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-6-multiple.html"&gt;6: Multiple Calls&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-7-exceptions.html"&gt;7: Exceptions&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-8-recursive.html"&gt;8: Recursive Mocks&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-9-functions.html"&gt;9: Functions&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/08/mocking-comparison-part-11-multiple.html"&gt;11: Multiple Interfaces&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/08/mocking-comparison-part-12-unmockables.html"&gt;12: The UnMockables&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-4985608053408502176?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/4985608053408502176/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2010/08/mocking-comparison-part-10-events.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/4985608053408502176'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/4985608053408502176'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2010/08/mocking-comparison-part-10-events.html' title='Mocking Comparison – Part 10: Events'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-6171536472650085113</id><published>2010-08-02T21:48:00.001+10:00</published><updated>2010-08-02T21:48:55.986+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='scrum'/><title type='text'>Technical Debt in Scrum Teams</title><content type='html'>&lt;p&gt;On a mailing list I’m on there was a thread recently about dealing with technical debt in a scrum team.&amp;#160; One of the responses went something along these lines (paraphrasing a bit here)&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;For delivering functionality you should use stories, but for technical debt don’t.&amp;#160; All teams accumulate technical debt and dealing with it doesn’t fit into the story paradigm so just create tasks in your sprints to deal with it that way instead.&lt;/p&gt; &lt;/blockquote&gt;  &lt;p&gt;This, of course, garnered a response from yours truly as follows (with a little editing):&lt;/p&gt;  &lt;p&gt;-----&lt;/p&gt;  &lt;p&gt;Why doesn’t it fit into stories?&amp;#160; All “non-functionals” can be represented as stories with business value.&amp;#160; If you can’t do that, then they shouldn’t be done.&lt;/p&gt;  &lt;p&gt;Examples:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;As a Dev Manager I want the XYZ module refactored so that future development continues or improves upon the pace it is at now&lt;/li&gt;    &lt;li&gt;As a Team Lead I want to clean up the FxCop warnings and reduce the cyclomatic complexity of the XXX assembly so that it is easier to change when we need to deliver the functionality expected in later sprints.&lt;/li&gt;    &lt;li&gt;As an Architect I want to improve some of the implementations the team has made so that it aligns with the SOLID principles and provides a system that is more adaptable to changes in requirements&lt;/li&gt;    &lt;li&gt;As a developer I want to clean up the rubbish I wrote in the previous sprint so that I can make the code easy for others to understand when they look at it&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;All of these can have a business value attached to them, be prioritised by your product owner, and broken down into tasks by the team, just as they should be.&amp;#160; I’ve seen too many teams putting lipstick on their pigs and failing to deliver simply because “technical purity” was deemed more important than delivery (and I’ve definitely seen the reverse as well!).&amp;#160; Every team carries technical debt – heck, code is technical debt all by itself – but the level of that debt that should be managed and balanced against new functionality by the product owner in consultation with the team, not by the team on their own.&lt;/p&gt;  &lt;p&gt;-----&lt;/p&gt;  &lt;p&gt;The lesson? Technical debt is a business risk.&amp;#160; Avoid accumulating it whenever and wherever practical.&amp;#160; Prevent it through sound development and architectural practices and improve and adhere to your Definition of Done. Once it’s there though, it’s up to the product owner and not the team to decide how much effort should be put into reducing the debt versus delivering new functionality.&lt;/p&gt;  &lt;p&gt;Teams that mutiny against their Product Owners and do what they deem appropriate have either failed to convey the cost of debt sufficiently or have failed to understand the business drivers that drive the Product Owner to prioritise new functionality over of a sustainable code base and have ineffectual Scrum Masters that are not managing the process correctly.&amp;#160; If this is you and your organisation, it’s time to end it and put things right.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-6171536472650085113?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/6171536472650085113/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2010/08/technical-debt-in-scrum-teams.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/6171536472650085113'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/6171536472650085113'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2010/08/technical-debt-in-scrum-teams.html' title='Technical Debt in Scrum Teams'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-4741441299137117494</id><published>2010-08-02T10:26:00.001+10:00</published><updated>2010-08-02T10:26:38.984+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tools'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><title type='text'>Viemu and Visual Studio 2010</title><content type='html'>&lt;p&gt;If you’ve been keeping your ears open over the last few months you’ll have heard noises from a number of folks who have been talking vim up as a replacement editor.&lt;/p&gt;  &lt;p&gt;On the other hand there have been people like &lt;a href="http://yehudakatz.com/2010/07/29/everyone-who-tried-to-convince-me-to-use-vim-was-wrong/" target="_blank"&gt;Yehuda Katz&lt;/a&gt; claiming that it’s all hype and everyone should go back to their homes and shut the doors because the crazies will be gone soon (ok, I’m paraphrasing a lot there).&lt;/p&gt;  &lt;p&gt;Of course, all of this made me chuckle and scratch my head at the same time.&lt;/p&gt;  &lt;p&gt;Why? Because 20 years ago I was C++ on unix, &lt;em&gt;using vi,&lt;/em&gt; as part of my Uni degree.&amp;#160; I even wrote a vi clone as one of my Uni team projects!&amp;#160; At the time I knew how to get around the editor reasonably well, though by no means was I a ninja, but I also thought it was as ugly as warts on your bum and compared to just being able to edit text the way you could with every other editor in the world at the time other than emacs, all that switch in/out of edit mode just felt like overhead.&lt;/p&gt;  &lt;p&gt;As Uni turned into career I started worked on platforms and languages where vi didn’t exist (Wang VS, VAX/VMS, Linux, Windows, 4GLs, VB4+, Visual Studio, etc) and I gradually forgot all my vi editing skills, got used to living in a constant edit mode and never missed vi for a minute.&lt;/p&gt;  &lt;p&gt;That said, it’s always good to question what you do and how you do it, and so if vi is being picked up by a whole new generation of developers then maybe this old fossil had better challenge his assumptions and re-evaluate wether vi is better than what I’m using currently and cut through all the talk with a little bit of personal experience. That said, I didn’t really want to lose my Visual Studio IDE and all the goodness &lt;a title="ReSharper" href="http://www.jetbrains.com/resharper/" target="_blank"&gt;ReSharper&lt;/a&gt; brings to it and my usual development workflow (i.e. the stuff that goes beyond just typing code) so I actually wanted the best of both worlds.&lt;/p&gt;  &lt;p&gt;And thankfully it turns that there is something like this out there.&amp;#160; There’s a Visual Studio add-in that brings all the good bits of vi into Visual Studio called &lt;a href="http://www.viemu.com/" target="_blank"&gt;viemu&lt;/a&gt; and I’ve been using it for a while now and really liking it.&amp;#160; The guys who make it have also put together a great article &lt;a title="Why use Vi?" href="http://www.viemu.com/a-why-vi-vim.html" target="_blank"&gt;explaining why people use vi/vim&lt;/a&gt;, and what some of the problems are in adopting it.&amp;#160; It’s well worth a read and just as soon as you finish reading this post you should go have a read of that one as well.&lt;/p&gt;  &lt;p&gt;Now, I will say up front that viemu is a commercial product, but since it has a 30-day trial there’s plenty of time for you to work out wether viemu will help you or not.&amp;#160; Personally, I’ve found it improving my productivity quite a lot and it’s really reduced my hand movements and mouse usage even more than R# did, so I’ve gone and acquired a licence.&lt;/p&gt;  &lt;p&gt;One of the issues I had initially was clashes with other extensions I have loaded (I have quite a few) so if you have ReSharper, the &lt;a title="VS2010 Power Tools" href="http://visualstudiogallery.msdn.microsoft.com/en-us/d0d33361-18e2-46c0-8ff2-4adea1e34fef" target="_blank"&gt;Visual Studio 2010 Productivity Power Tools&lt;/a&gt; or other similar extensions loaded then you may need to turn off a few options in them.&amp;#160; What sort of clashes, you ask? Well, as vi uses a normal keyboard characters for navigation such as braces, brackets and parentheses other extensions pick up the key presses and think they are just normal typing so they go ahead and helpfully auto insert the matching closing characters, leaving random characters littered around your code if you’re not paying attention.&amp;#160; Just go into your tools and find the relevant auto completion options and turn them off.&lt;/p&gt;  &lt;p&gt;I should also mention, there’s a very handy shortcut to enable/disable viemu at any point in VS2010 so if someone else comes to your machine to do some pairing you can easily switch it off, let them work the way they wish and switch it back on when you have the keyboard again.&amp;#160; Nice!&lt;/p&gt;  &lt;p&gt;Finally, if you’re just learning the vi/vim commands I’d recommend you run through the &lt;a title="Vi Keyboard Shortcut Cheat Sheet" href="http://www.viemu.com/a_vi_vim_graphical_cheat_sheet_tutorial.html" target="_blank"&gt;visual cheat sheet and tutorials&lt;/a&gt; on the viemu site.&amp;#160; It really helped me a lot in that regard, and made adoption much quicker.&amp;#160; So with that said, if you want to save your hands and wrists then go download it and give it a try today.&amp;#160; I’m really enjoying it, and you may just do so too!&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-4741441299137117494?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/4741441299137117494/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2010/08/viemu-and-visual-studio-2010.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/4741441299137117494'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/4741441299137117494'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2010/08/viemu-and-visual-studio-2010.html' title='Viemu and Visual Studio 2010'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-5747032922892135801</id><published>2010-07-30T20:19:00.001+10:00</published><updated>2010-08-06T08:56:44.800+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mocking'/><title type='text'>Mocking Comparison – Part 9: Functions</title><content type='html'>&lt;p&gt;Let’s say you’ve got a mock object and you want a method on your mock to do something with a particular method that goes beyond just returning a specified value.&amp;#160; What do you do?&amp;#160; Why, you use a function of course!&amp;#160; When you stub a method call on a mock you simply tell the mock object to call a method or use a lambda to calculate the return value rather than just returning a specified value.&lt;/p&gt;  &lt;p&gt;Let’s have a look at how Rhino Mocks, Moq and NSubstitute do this.&lt;/p&gt;  &lt;p&gt;What we’re going to do is supply some simple methods that our mock object will use when we call certain methods so that we can dynamically alter the return value from the CurrentFleaCount method and we’ll then assert that the value has changed.&lt;/p&gt;  &lt;h3&gt;Rhino Mocks&lt;/h3&gt;  &lt;p&gt;&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:8e2b0f69-5b43-49dc-ae27-3e2666993e7d" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: c#;"&gt;[Fact]&lt;br /&gt;public void Rhino_functions_and_callbacks()&lt;br /&gt;{&lt;br /&gt; var fleaCount = 25;&lt;br /&gt; var monkey = MockRepository.GenerateMock&amp;lt;IMonkey&amp;gt;();&lt;br /&gt;&lt;br /&gt; monkey.Stub(m =&amp;gt; m.CurrentFleaCount()).Do(new Func&amp;lt;int&amp;gt;(() =&amp;gt; fleaCount));&lt;br /&gt; monkey.Stub(m =&amp;gt; m.TryAddFleas(1)).IgnoreArguments()&lt;br /&gt;  .Do(new Func&amp;lt;int, bool&amp;gt;(fleas =&amp;gt;&lt;br /&gt;  {&lt;br /&gt;   fleaCount = fleas;&lt;br /&gt;   return true;&lt;br /&gt;  }));&lt;br /&gt;&lt;br /&gt; Assert.Equal(25, monkey.CurrentFleaCount());&lt;br /&gt;&lt;br /&gt; monkey.TryAddFleas(10);&lt;br /&gt; Assert.Equal(10, monkey.CurrentFleaCount());&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Since this is a reasonable chunk of code, let’s just look at the key parts.&amp;#160; When we stub the CurrentFleaCount method call we tell it to use a function that takes no arguments, and use the value of the fleaCount variable (defined in the test) to figure out what returns.&lt;/p&gt;&lt;p&gt;You may ask why didn’t we just do a .Return(fleaCount) like we normally would? Simple answer: we want to get the value to change as the test progresses.&amp;#160; By using a Func&amp;lt;T&amp;gt; means that the return value is calculated each time we call the stubbed method, rather than just having a single fixed value returned for every call.&lt;/p&gt;&lt;p&gt;Similarly in the stubbed TryAddFleas() method, we are providing a function that takes one parameter and returns a boolean.&amp;#160; Our stub method simply changes the value of fleaCount variable so that the next CurrentFleaCount() call will return a different value, and this is what the Asserts are checking for.&lt;/p&gt;&lt;p&gt;Hopefully that makes sense.&amp;#160; Form a day to day usage viewpoint it’s not something you do that often but when you need the functionality it’s really useful.&lt;/p&gt;&lt;h3&gt;Moq&lt;/h3&gt;&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:c1d2ac43-30f1-4a19-bdd6-ed906142b2bc" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: c#;"&gt;[Fact]&lt;br /&gt;public void Moq_functions_and_callbacks()&lt;br /&gt;{&lt;br /&gt; var fleaCount = 25;&lt;br /&gt; var monkey = new Mock&amp;lt;IMonkey&amp;gt;();&lt;br /&gt; &lt;br /&gt; monkey.Setup(m =&amp;gt; m.CurrentFleaCount()).Returns(() =&amp;gt; fleaCount);&lt;br /&gt; monkey.Setup(m =&amp;gt; m.TryAddFleas(It.IsAny&amp;lt;int&amp;gt;()))&lt;br /&gt;  .Returns&amp;lt;int&amp;gt;(fleas =&amp;gt;&lt;br /&gt;  {&lt;br /&gt;   fleaCount = fleas;&lt;br /&gt;   return true;&lt;br /&gt;  });&lt;br /&gt;&lt;br /&gt; Assert.Equal(25, monkey.Object.CurrentFleaCount());&lt;br /&gt;&lt;br /&gt; monkey.Object.TryAddFleas(10);&lt;br /&gt; Assert.Equal(10, monkey.Object.CurrentFleaCount());&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The Moq syntax is much the same as the Rhino syntax, as you can see.&amp;#160; It’s probably a little nicer in that you use the same Returns() method to either return fixed values or call a function, unlike Rhino Mocks where you have to use the .Do() method.&lt;/p&gt;&lt;p&gt;It also doesn’t require the creation of new Func&amp;lt;T&amp;gt; objects – you just supply the method body you wish to use, which means less code.&lt;/p&gt;&lt;h3&gt;NSubstitute&lt;/h3&gt;&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:189b0f9d-7caf-4fee-abda-950c393728a6" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: c#;"&gt;[Fact]&lt;br /&gt;public void Nsubstitute_functions_and_callbacks()&lt;br /&gt;{&lt;br /&gt; var fleaCount = 25;&lt;br /&gt; var monkey = Substitute.For&amp;lt;IMonkey&amp;gt;();&lt;br /&gt;&lt;br /&gt; monkey.CurrentFleaCount().Returns(args =&amp;gt; fleaCount);&lt;br /&gt; monkey.TryAddFleas(Arg.Any&amp;lt;int&amp;gt;())&lt;br /&gt;  .Returns(args =&amp;gt;&lt;br /&gt;  {&lt;br /&gt;      fleaCount = (int)args[0];&lt;br /&gt;      return true;&lt;br /&gt;  });&lt;br /&gt;&lt;br /&gt; Assert.Equal(25, monkey.CurrentFleaCount());&lt;br /&gt;&lt;br /&gt; monkey.TryAddFleas(10);&lt;br /&gt; Assert.Equal(10, monkey.CurrentFleaCount());&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The NSubstitute code is a little different to the others in that instead of having parameter lists to deal with you are passed a single array containing all the parameters and it’s up to you to pull out the ones you wish to use and cast them as required by your test.&lt;/p&gt;&lt;p&gt;This has it’s advantages in that you avoid code littered with arguments you never use, but it has a downside in that you need to cast every parameter you do use.&amp;#160; That said, just like Moq, you get to use the same Returns() method for either returning a fixed value or supplying a method and you don’t need to create new Func&amp;lt;T&amp;gt; objects so the code isn’t noisy.&amp;#160; And better than Moq is it’s overall cleaner syntax, as we have seen in many of the previous posts.&lt;/p&gt;&lt;p&gt;My preference? NSubstitute.&amp;#160; The casting of parameters is a little annoying, but it doesn’t have Moq .Object. tax and this is one of the few times you’ll actually see a lambda in a test using NSubstitute.&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Other posts in this series:&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-1-basics.html"&gt;1: The Basics&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-2-properties.html"&gt;2: Properties&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-3-interactions.html"&gt;3: Interactions&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-4-parameter.html"&gt;4: Parameter Constraints&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-5-repetitions.html"&gt;5: Repetitions&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-6-multiple.html"&gt;6: Multiple Calls&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-7-exceptions.html"&gt;7: Exceptions&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-8-recursive.html"&gt;8: Recursive Mocks&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/08/mocking-comparison-part-10-events.html"&gt;10: Events&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/08/mocking-comparison-part-11-multiple.html"&gt;11: Multiple Interfaces&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/08/mocking-comparison-part-12-unmockables.html"&gt;12: The UnMockables&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-5747032922892135801?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/5747032922892135801/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2010/07/mocking-comparison-part-9-functions.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/5747032922892135801'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/5747032922892135801'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2010/07/mocking-comparison-part-9-functions.html' title='Mocking Comparison – Part 9: Functions'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-1411332327146950622</id><published>2010-07-30T09:29:00.001+10:00</published><updated>2010-09-21T08:41:57.372+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='TFS'/><title type='text'>How To: Versioning Builds With TFS 2010</title><content type='html'>&lt;p&gt;I’ve written in the past on how to do &lt;a title="Versioning Builds with TFS 2008" href="http://www.richard-banks.org/2007/07/versioning-builds-with-tfs-and-msbuild.html" target="_blank"&gt;automatic assembly versioning with TFS Team Build 2008&lt;/a&gt; so it’s about time I got around to showing you how to do with with TFS Team Build 2010.&lt;/p&gt;  &lt;p&gt;By the way, for a great series of blog posts on customising team build with TFS 2010 it’s worth having a look at &lt;a href="http://www.ewaldhofman.nl/post/2010/04/20/Customize-Team-Build-2010-e28093-Part-1-Introduction.aspx" target="_blank"&gt;Ewald Hofman’s series on customising team build&lt;/a&gt;.&lt;/p&gt;  &lt;h3&gt;What We Want to Achieve&lt;/h3&gt;  &lt;p&gt;So, the goal here is to change the standard build process to enable us to do automatic versioning of assemblies.&amp;#160; To do this what we need to do is insert some steps in the process just after the source has been retrieved from TFS.&amp;#160; We want a flow that goes something like this:&lt;/p&gt;  &lt;p&gt;Get Latest –&amp;gt; Update AssemblyInfo files with new version –&amp;gt; Do rest of the build&lt;/p&gt;  &lt;p&gt;One extra wrinkle to this is that when team build gets the code from TFS the files are in read only mode, so what we’re going to do is switch the read only flag off, change the files, and then turn it back on.&amp;#160; This is just in case an future incremental get tries to update the file – the read only flag will stop TFS checking if the file is different.&lt;/p&gt;  &lt;p&gt;One thing we’re not going to do is check the updated assembly info files back into source control.&amp;#160; This means all developer builds will by default have a version number of 1.0.0.0 and build server builds will have an incrementing build number.&amp;#160; It’s a handy way to know if someone has tried to deploy code built on their machine instead of code built by the server.&lt;/p&gt;  &lt;p&gt;This approach does raise a small issue though – where do we keep the build number between builds?&amp;#160; i.e. how to we know what the previous build number was so we can increment it.&amp;#160; For this we’re going to use a simple version.txt file stored in the root of the drop folder.&amp;#160; If you want to secure this so that only the build process updates the version number I would suggest making the drop location read only for all users except the account the build runs under.&lt;/p&gt;  &lt;p&gt;Anyway, enough talk.&amp;#160; Let’s see what we need to do.&lt;/p&gt;  &lt;h3&gt;Getting Started&lt;/h3&gt;  &lt;p&gt;1. Open VS2010 and create a solution with two C# Class LIbrary projects in it.&amp;#160; One for the custom build tasks we’re going to create, and one to hold the process template we’re going change.&amp;#160; I have called mine CustomBuildActivities and CustomBuildProcess&lt;/p&gt;  &lt;p&gt;2. In the CustomBuildProcess project add a Workflow activity and then delete it.&amp;#160; This just helps with getting a bunch of references in place and adds the appropriate content type we’ll need when we add our build workflow in the next step.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_5dD_rBQSs2o/TFIMUC1vmQI/AAAAAAAAAyc/MEc5pfYfq8w/s1600-h/image%5B84%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_5dD_rBQSs2o/TFIMVd7A1CI/AAAAAAAAAyg/nvi-m7NSKHU/image_thumb%5B45%5D.png?imgmax=800" width="484" height="274" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;3. Now go copy the DefaultTemplate.xaml file, rename it to something you like and include it in your project.&amp;#160; I’ve called mine VersionedBuildProcess.xaml.&amp;#160; Once it’s included in the solution change the Build Action to XamlAppDef.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_5dD_rBQSs2o/TFIMWHRZnkI/AAAAAAAAAyk/DsS1UJ2mrL8/s1600-h/image20.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_5dD_rBQSs2o/TFIMXnHAyII/AAAAAAAAAyo/Jv-_vZ3WG8k/image_thumb12.png?imgmax=800" width="319" height="397" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;At this point if you try compiling you’ll get a bunch of missing references, so add the following (sorry it’s a laundry list of items!)&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;System.Drawing &lt;/li&gt;    &lt;li&gt;Microsoft.TeamFoundation.Build.Client &lt;/li&gt;    &lt;li&gt;Microsoft.TeamFoundation.VersionControl.Client &lt;/li&gt;    &lt;li&gt;Microsoft.TeamFoundation.WorkItemTracking.Client &lt;/li&gt;    &lt;li&gt;%Program Files%\Microsoft Visual Studio 10.0\Common7\IDE\PrivateAssemblies\      &lt;br /&gt;Microsoft.TeamFoundation.Build.Workflow.dll &lt;/li&gt;    &lt;li&gt;%Program Files%\Microsoft Visual Studio 10.0\Common7\IDE\PrivateAssemblies\      &lt;br /&gt;Microsoft.TeamFoundation.TestImpact.BuildIntegration.dll &lt;/li&gt;    &lt;li&gt;%WinDir%\assembly\GAC_MSIL\Microsoft.TeamFoundation.TestImpact.Client      &lt;br /&gt;\10.0.0.0__b03f5f7f11d50a3a\Microsoft.TeamFoundation.TestImpact.Client.dll &lt;/li&gt; &lt;/ul&gt;&lt;p&gt;&lt;strong&gt;UPDATE:&lt;/strong&gt; If you set the Build Action to None then you don't need to worry about setting all those references and you can still edit the XAML as  per usual.  This is a lot easier for many people, but the downside is that you can't verify the workflow is 100% correct unless you manually look through the whole workflow or try a build with it.  My preference is the first, but do whatever you're comfortable with :-) &lt;/p&gt;  &lt;h3&gt;Create the First Custom Activity&lt;/h3&gt;  &lt;p&gt;Next&amp;#160; we’re going to create our first custom activity – this one will be the one we use to toggles the read only flags on AssemblyInfo files.&amp;#160; In your CustomBuildActivities project add a new Workflow Code Activity called SetReadOnlyFlag.&amp;#160; We’ll use this to toggle the read only bits for our AssemblyInfo files later on.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_5dD_rBQSs2o/TFIMafOjV-I/AAAAAAAAAys/_zNBttSxGBg/s1600-h/image13.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_5dD_rBQSs2o/TFIMbT9OiGI/AAAAAAAAAyw/Rxsl7JXRIys/image_thumb5.png?imgmax=800" width="484" height="274" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Now add references to Microsoft.TeamFoundation.Build.Client and Microsoft.TeamFoundation.VersionControl.Client and then use the following code for the class:&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:faae01f4-4cac-4de0-9aea-9f2cfc3a6ec1" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: c#;"&gt;using System.Activities;&lt;br /&gt;using Microsoft.TeamFoundation.Build.Client;&lt;br /&gt;using Microsoft.TeamFoundation.VersionControl.Client;&lt;br /&gt;&lt;br /&gt;namespace CustomActivities&lt;br /&gt;{&lt;br /&gt; [BuildActivity(HostEnvironmentOption.Agent)]&lt;br /&gt; public sealed class SetReadOnlyFlag : CodeActivity&lt;br /&gt; {&lt;br /&gt;  [RequiredArgument]&lt;br /&gt;  public InArgument&amp;lt;string&amp;gt; FileMask { get; set; }&lt;br /&gt;&lt;br /&gt;  [RequiredArgument]&lt;br /&gt;  public InArgument&amp;lt;bool&amp;gt; ReadOnlyFlagValue { get; set; }&lt;br /&gt;&lt;br /&gt;  [RequiredArgument]&lt;br /&gt;  public InArgument&amp;lt;Workspace&amp;gt; Workspace { get; set; }&lt;br /&gt;&lt;br /&gt;  protected override void Execute(CodeActivityContext context)&lt;br /&gt;  {&lt;br /&gt;   var fileMask = context.GetValue(FileMask);&lt;br /&gt;   var workspace = context.GetValue(Workspace);&lt;br /&gt;   var readOnlyFlagValue = context.GetValue(ReadOnlyFlagValue);&lt;br /&gt;&lt;br /&gt;   foreach (var folder in workspace.Folders)&lt;br /&gt;   {&lt;br /&gt;    foreach (var file in Directory.GetFiles(folder.LocalItem, fileMask, SearchOption.AllDirectories))&lt;br /&gt;    { &lt;br /&gt;     var attributes = File.GetAttributes(file);&lt;br /&gt;     if (readOnlyFlagValue)&lt;br /&gt;      File.SetAttributes(file, attributes | FileAttributes.ReadOnly);&lt;br /&gt;     else&lt;br /&gt;      File.SetAttributes(file,attributes &amp;amp; ~FileAttributes.ReadOnly);&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The [BuildActivity] attribute on the class indicates that this class can be used as a workflow activity in the designer, and that it should be loaded on the build agent when a build runs.&lt;/p&gt;&lt;p&gt;This code here is how we define the arguments that the activity will use – they will appear in the properties window when the activity is selected in the designer, and the RequiredAttribute indicates that these properties must have values for the workflow to be valid.&lt;/p&gt;&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:00faf1f5-128d-4c38-90b1-50db5fad5d5f" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: c#;"&gt;[RequiredArgument]&lt;br /&gt;public InArgument&amp;lt;string&amp;gt; FileMask { get; set; }&lt;br /&gt;&lt;br /&gt;[RequiredArgument]&lt;br /&gt;public InArgument&amp;lt;bool&amp;gt; ReadOnlyFlagValue { get; set; }&lt;br /&gt;&lt;br /&gt;[RequiredArgument]&lt;br /&gt;public InArgument&amp;lt;Workspace&amp;gt; Workspace { get; set; }&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We’re going to use the Workspace to get information about where the build agent has physically put files on the disk so that we can get path information and search for the files we wish to change.&amp;#160; The other properties are used to actually find out what files to change and which way to set the flag.&lt;/p&gt;&lt;h3&gt;Create The Second Custom Activity&lt;/h3&gt;&lt;p&gt;For this activity we want to take a set of files as defined by a mask and do a search/replace on them to update the version numbers for the AssemblyVersion and AssemblyFileVersion attributes.&lt;/p&gt;&lt;p&gt;Here’s the code:&lt;/p&gt;&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:98483608-f6e3-40d4-903c-cf36001fd7cd" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: c#;"&gt;[BuildActivity(HostEnvironmentOption.Agent)]&lt;br /&gt;public sealed class UpdateAssemblyVersionInfo : CodeActivity&lt;br /&gt;{&lt;br /&gt; [RequiredArgument]&lt;br /&gt; public InArgument&amp;lt;string&amp;gt; AssemblyInfoFileMask { get; set; }&lt;br /&gt;&lt;br /&gt; [RequiredArgument]&lt;br /&gt; public InArgument&amp;lt;string&amp;gt; SourcesDirectory { get; set; }&lt;br /&gt;&lt;br /&gt; [RequiredArgument]&lt;br /&gt; public InArgument&amp;lt;string&amp;gt; VersionFilePath { get; set; }&lt;br /&gt;&lt;br /&gt; [RequiredArgument]&lt;br /&gt; public InArgument&amp;lt;string&amp;gt; VersionFileName { get; set; }&lt;br /&gt;&lt;br /&gt; protected override void Execute(CodeActivityContext context)&lt;br /&gt; {&lt;br /&gt;  var sourcesDirectory = context.GetValue(SourcesDirectory);&lt;br /&gt;  var assemblyInfoFileMask = context.GetValue(AssemblyInfoFileMask);&lt;br /&gt;  var versionFile = context.GetValue(VersionFilePath) + @"\" + context.GetValue(VersionFileName);&lt;br /&gt;&lt;br /&gt;  //Load the version info into memory&lt;br /&gt;  var versionText = "1.0.0.0";&lt;br /&gt;  if (File.Exists(versionFile))&lt;br /&gt;   versionText = File.ReadAllText(versionFile);&lt;br /&gt;  var currentVersion = new Version(versionText);&lt;br /&gt;  var newVersion = new Version(currentVersion.Major, currentVersion.Minor, currentVersion.Build + 1, currentVersion.Revision);&lt;br /&gt;  File.WriteAllText(versionFile, newVersion.ToString());&lt;br /&gt;&lt;br /&gt;  bool changedContents;&lt;br /&gt;  foreach (var file in Directory.EnumerateFiles(sourcesDirectory, assemblyInfoFileMask, SearchOption.AllDirectories))&lt;br /&gt;  {&lt;br /&gt;   var text = File.ReadAllText(file);&lt;br /&gt;   changedContents = false;&lt;br /&gt;   // we want to find 'AssemblyVersion("1.0.0.0")' etc&lt;br /&gt;   foreach (var attribute in new[] { "AssemblyVersion", "AssemblyFileVersion" })&lt;br /&gt;   {&lt;br /&gt;    var regex = new Regex(attribute + @"\(""\d+\.\d+\.\d+\.\d+""\)");&lt;br /&gt;    var match = regex.Match(text);&lt;br /&gt;    if (!match.Success) continue;&lt;br /&gt;    text = regex.Replace(text, attribute + "(\"" + newVersion + "\")");&lt;br /&gt;    changedContents = true;&lt;br /&gt;   }&lt;br /&gt;   if (changedContents)&lt;br /&gt;    File.WriteAllText(file, text);&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;At this point you should be able to compile your application and see it all pass, but until we change the workflow itself this is pretty useless.&lt;/p&gt;&lt;h3&gt;Updating the Build Workflow&lt;/h3&gt;&lt;p&gt;In your process project open up the build process.&amp;#160; In the toolbox you should now see something like this:&lt;/p&gt;&lt;p&gt;&lt;a href="http://lh5.ggpht.com/_5dD_rBQSs2o/TFIMcLcy7mI/AAAAAAAAAy0/MFpuFvHboRk/s1600-h/image%5B9%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_5dD_rBQSs2o/TFIMc-QG2LI/AAAAAAAAAy4/y4D4ZkmOy48/image_thumb%5B2%5D.png?imgmax=800" width="237" height="117" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;We want to drop these tasks into our workflow at the appropriate point, so to do this find the part of the workflow that looks something like this:&lt;/p&gt;&lt;p&gt;&lt;a href="http://lh6.ggpht.com/_5dD_rBQSs2o/TFIMdu8BEUI/AAAAAAAAAy8/-OAtsaGHY-I/s1600-h/image%5B64%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_5dD_rBQSs2o/TFIMed-R1GI/AAAAAAAAAzA/QOvTmzmvGgI/image_thumb%5B25%5D.png?imgmax=800" width="280" height="370" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;For reference it’s in ….Sequence &amp;gt; Run On Agent &amp;gt; Initialize Workspace (near the bottom)&lt;/p&gt;&lt;p&gt;Now add a sequence activity after the get workspace activity like so:&lt;/p&gt;&lt;p&gt;&lt;a href="http://lh3.ggpht.com/_5dD_rBQSs2o/TFIMfYUPhWI/AAAAAAAAAzE/NVKC9aNrHQ8/s1600-h/image%5B65%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_5dD_rBQSs2o/TFIMgNO1sxI/AAAAAAAAAzI/0AT8DTSaye4/image_thumb%5B26%5D.png?imgmax=800" width="280" height="206" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;Change the display name property to “Update AssemblyInfo Versions&amp;quot;” or something similar.&amp;#160; This not only appears in the build log, but has the advantage of helping you understand your workflow better.&lt;/p&gt;&lt;p&gt;Now drag the ReadOnly flag activity we created into the new sequence activity you added – it should now look something like this:&lt;/p&gt;&lt;p&gt;&lt;a href="http://lh6.ggpht.com/_5dD_rBQSs2o/TFIMg3LM7FI/AAAAAAAAAzM/8fnpNlTNt-k/s1600-h/image%5B66%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_5dD_rBQSs2o/TFIMhyvTe8I/AAAAAAAAAzQ/hF0jy1fom4c/image_thumb%5B27%5D.png?imgmax=800" width="265" height="193" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;That error indicator means we haven’t yet supplied some property values for the activity, so we’d best do that now.&lt;/p&gt;&lt;h3&gt;Set Activity Properties&lt;/h3&gt;&lt;p&gt;If you look at the properties window for our activity you should see something like this:&lt;/p&gt;&lt;p&gt;&lt;a href="http://lh6.ggpht.com/_5dD_rBQSs2o/TFIMi47QgBI/AAAAAAAAAzU/-wjzEJzIxt4/s1600-h/image%5B67%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_5dD_rBQSs2o/TFIMkFgctkI/AAAAAAAAAzY/ql4KI5CRp5U/image_thumb%5B28%5D.png?imgmax=800" width="357" height="226" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;What we want is for the users of our workflow to be able to specify the values for the FileMask via the Build Definition window, and we want to set the values for the Workspace based on whatever is current in our workflow.&lt;/p&gt;&lt;p&gt;Let’s tackle the FileMask first.&amp;#160; To do this we’re going to add a new Argument to our overall workflow.&amp;#160; Find the Arguments tab at the bottom of the designer:&lt;/p&gt;&lt;p&gt;&lt;a href="http://lh5.ggpht.com/_5dD_rBQSs2o/TFIMlEjqLcI/AAAAAAAAAzc/qyoVZ-S9GRA/s1600-h/image%5B68%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_5dD_rBQSs2o/TFIMmWATBjI/AAAAAAAAAzg/xn3YZOEU0HM/image_thumb%5B29%5D.png?imgmax=800" width="390" height="148" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;Click in the Create Argument box and add a new argument called AssemblyInfoMask and give it a default value of “AssemblyInfo.*”&lt;/p&gt;&lt;p&gt;Before we use this in our activities let’s just make a few more changes to make this argument appear in the build process window the way we want it.&amp;#160; Go to the metadata argument a few lines above and click the ellipsis […] button.&amp;#160; You will see a window appear where we can set values that will let Visual Studio know how to display this argument to the end user.&amp;#160; Let’s do just that:&lt;/p&gt;&lt;p&gt;&lt;a href="http://lh6.ggpht.com/_5dD_rBQSs2o/TFIMnyAM_3I/AAAAAAAAAzk/h1ki1WHf4j8/s1600-h/image%5B69%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_5dD_rBQSs2o/TFIMonRIpqI/AAAAAAAAAzo/A50dUZN1ioo/image_thumb%5B30%5D.png?imgmax=800" width="386" height="455" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;Now let’s fix the properties on our Activity&lt;/p&gt;&lt;p&gt;&lt;a href="http://lh6.ggpht.com/_5dD_rBQSs2o/TFIMpcLiL7I/AAAAAAAAAzs/hUVaZAckO3Q/s1600-h/image%5B70%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_5dD_rBQSs2o/TFIMqb4tCYI/AAAAAAAAAzw/ApbQfqHqoF0/image_thumb%5B31%5D.png?imgmax=800" width="352" height="226" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;Now you may be asking where the Workspace value comes from since it’s not an argument for the workflow.&amp;#160; If you look a few activities above you will see a create workspace activity and one of it’s properties is an OutProperty called Result:&lt;/p&gt;&lt;p&gt;&lt;a href="http://lh3.ggpht.com/_5dD_rBQSs2o/TFIMrpo6GYI/AAAAAAAAAz0/wLrGOo0eH-o/s1600-h/image%5B71%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_5dD_rBQSs2o/TFIMsgM0ssI/AAAAAAAAAz4/RFsbzCu5qss/image_thumb%5B32%5D.png?imgmax=800" width="358" height="252" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;This is where the Workspace variable is created.&amp;#160; Unfortunately there’s not really an easy way of tracking this sort of thing with the workflow, but fortunately most variables in the workflow are well named and you should be able to figure out what most of them do.&lt;/p&gt;&lt;h3&gt;Finishing the Workflow&lt;/h3&gt;&lt;p&gt;Let’s finish off the workflow.&amp;#160; Drag the UpdateAssemblyVersionInfo task into the workflow along with a second SetReadOnlyFlag task.&lt;/p&gt;&lt;p&gt;Before we set the properties on these, we’re going to need one more workflow Argument, being the name of the version file we’re going to store the version information in.&lt;/p&gt;&lt;p&gt;&lt;a href="http://lh5.ggpht.com/_5dD_rBQSs2o/TFIMtUF5l2I/AAAAAAAAAz8/nWLdaDuLMCk/s1600-h/image%5B73%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_5dD_rBQSs2o/TFIMulT5xxI/AAAAAAAAA0A/v2TYw6rTILs/image_thumb%5B34%5D.png?imgmax=800" width="484" height="116" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;Don’t forget to set the Metadata as well so that it appears in the correct place in Visual Studio when we define builds.&lt;/p&gt;&lt;p&gt;Now set the properties on the UpdateAssemblyVersionInfo activity as follows:&lt;/p&gt;&lt;p&gt;&lt;a href="http://lh5.ggpht.com/_5dD_rBQSs2o/TFIMvoJkw9I/AAAAAAAAA0E/VUS8n31rh3M/s1600-h/image%5B74%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_5dD_rBQSs2o/TFIMws0qQ9I/AAAAAAAAA0I/4hPQtpf1XsY/image_thumb%5B35%5D.png?imgmax=800" width="339" height="241" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;Since we want to store the file we hold our version info at the root of the drop location we have set the VersionFilePath to BuildDetail.DropLocationRoot.&amp;#160; You can of course, change this to put it wherever you like.&lt;/p&gt;&lt;p&gt;Finally set the properties for the second SetReadOnlyFlag task, this time with the ReadOnly flag set to true.&lt;/p&gt;&lt;p&gt;At this point build your project.&amp;#160; If it’s all OK everything should compile and you should be ready for the next step.&lt;/p&gt;&lt;h3&gt;Using The New Build Process&lt;/h3&gt;&lt;p&gt;Before we can use the new build process, we have to ensure both the process and the custom activities are in source control.&lt;/p&gt;&lt;p&gt;The .xaml process file should be added to the BuildProcessTemplates folder, and I usually place custom activities in a subfolder within that.&amp;#160; For example:&lt;/p&gt;&lt;p&gt;&lt;a href="http://lh4.ggpht.com/_5dD_rBQSs2o/TFIMxWicx8I/AAAAAAAAA0M/icKIvf7oqXE/s1600-h/image%5B75%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_5dD_rBQSs2o/TFIMyEceoCI/AAAAAAAAA0Q/q-C8UBxfjIA/image_thumb%5B36%5D.png?imgmax=800" width="251" height="125" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;Now we have to tell our Build Controller where to find our custom activities otherwise the build will fail.&amp;#160; Go to Manage Build Controllers…&lt;/p&gt;&lt;p&gt;&lt;a href="http://lh6.ggpht.com/_5dD_rBQSs2o/TFIMzBj7laI/AAAAAAAAA0U/kKm3XCpstzM/s1600-h/image%5B76%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_5dD_rBQSs2o/TFIM1ZNtT_I/AAAAAAAAA0Y/MCKtWT0D4wY/image_thumb%5B37%5D.png?imgmax=800" width="364" height="236" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;and in the properties window set the path to the activities&lt;/p&gt;&lt;p&gt;&lt;a href="http://lh4.ggpht.com/_5dD_rBQSs2o/TFIM2q9fjkI/AAAAAAAAA0c/XL1FVaVL4Qw/s1600-h/image%5B78%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_5dD_rBQSs2o/TFIM4223eeI/AAAAAAAAA0g/n9eOUU8hAy0/image_thumb%5B39%5D.png?imgmax=800" width="484" height="485" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;Now we can finally create a build definition.&lt;/p&gt;&lt;p&gt;In the new build definition window, go to the Process tab, select the Show Details window and Click New..&lt;/p&gt;&lt;p&gt;&lt;a href="http://lh4.ggpht.com/_5dD_rBQSs2o/TFIM6HqNyRI/AAAAAAAAA0k/W72xgKMc3Hg/s1600-h/image%5B80%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_5dD_rBQSs2o/TFIM7NFb5QI/AAAAAAAAA0o/5FBNLAXWxPU/image_thumb%5B41%5D.png?imgmax=800" width="484" height="179" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;Select an existing .xaml file (being the new process you uploaded)&lt;/p&gt;&lt;p&gt;&lt;a href="http://lh4.ggpht.com/_5dD_rBQSs2o/TFIM8ud3MKI/AAAAAAAAA0s/gA5AAosNr_M/s1600-h/image%5B81%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_5dD_rBQSs2o/TFIM97zSI5I/AAAAAAAAA0w/XBahJtA5CjU/image_thumb%5B42%5D.png?imgmax=800" width="426" height="460" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;If you set your metadata properties correctly, the process settings should now look something like this:&lt;/p&gt;&lt;p&gt;&lt;a href="http://lh6.ggpht.com/_5dD_rBQSs2o/TFIM_LaXvhI/AAAAAAAAA00/DdqkL4QR0kQ/s1600-h/image%5B82%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_5dD_rBQSs2o/TFINAQcM5gI/AAAAAAAAA04/fPdtTwGytV8/image_thumb%5B43%5D.png?imgmax=800" width="484" height="178" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;If all goes well, you should get a completed build and see something like the following in the log, indicating our new steps occurred.&lt;/p&gt;&lt;p&gt;&lt;a href="http://lh6.ggpht.com/_5dD_rBQSs2o/TFINBIlfPwI/AAAAAAAAA08/2Had16R-W1g/s1600-h/image%5B83%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_5dD_rBQSs2o/TFINCWtSyQI/AAAAAAAAA1A/I9nbeVDbo5s/image_thumb%5B44%5D.png?imgmax=800" width="365" height="170" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;Finally go to the drop location, right click one of your assemblies and see that the version information is being updated.&lt;/p&gt;&lt;h3&gt;Phew! That’s it!&amp;#160; We’re done!&lt;/h3&gt;&lt;p&gt;I know it’s a fair bit of work getting this all set up, but once you’ve done it once and have your initial workflow project in place, you can use it as the basis for any future process customisations which makes things much quicker.&amp;#160; Also as you get more familiar with the workflows themselves and knowing where to make changes the process customisation itself will also become faster and easier.&lt;/p&gt;&lt;p&gt;Good luck customising your builds!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-1411332327146950622?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/1411332327146950622/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2010/07/how-to-versioning-builds-with-tfs-2010.html#comment-form' title='20 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/1411332327146950622'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/1411332327146950622'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2010/07/how-to-versioning-builds-with-tfs-2010.html' title='How To: Versioning Builds With TFS 2010'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_5dD_rBQSs2o/TFIMVd7A1CI/AAAAAAAAAyg/nvi-m7NSKHU/s72-c/image_thumb%5B45%5D.png?imgmax=800' height='72' width='72'/><thr:total>20</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-638366900969220881</id><published>2010-07-29T21:21:00.002+10:00</published><updated>2010-08-06T08:56:22.435+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mocking'/><title type='text'>Mocking Comparison – Part 8: Recursive Mocks</title><content type='html'>&lt;p&gt;Recursive mocks (or nested mocks as some people call them) are a feature of the mocking frameworks where they will automatically create mock objects for items they should return if you reference a property or method of that object when setting up expectations.&amp;#160; The advantages of doing this are that we have less “arrange” code in our test classes and can focus our efforts more on our act and assert code.&lt;/p&gt;  &lt;p&gt;Maybe that explanation of recursive mocks doesn’t clarify things much for you so let’s just look at some code instead and see how Rhino Mocks, Moq and NSubstitute help us out.&lt;/p&gt;&lt;h3&gt;Rhino Mocks&lt;/h3&gt;&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:574a1199-d20e-4c92-8a69-a8cbd043b535" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: c#;"&gt;[Fact]&lt;br /&gt;public void Rhino_recursive_mocks()&lt;br /&gt;{&lt;br /&gt; var monkey = MockRepository.GenerateStub&amp;lt;IMonkey&amp;gt;();&lt;br /&gt; monkey.Name = "Spike";&lt;br /&gt; monkey.Stub(m =&amp;gt; m.Keeper().AssignedMonkey).Return(monkey);&lt;br /&gt; Assert.NotNull(monkey.Keeper());&lt;br /&gt; Assert.Equal("Spike", monkey.Keeper().AssignedMonkey.Name);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you have a look at the code you can see that we create a stub object for the the IMonkey interface.&amp;#160; Within that we set up an expectation that when we check which monkey the keeper is assigned to that it should be the monkey object we just created (i.e. we’re setting up a circular object reference).&lt;/p&gt;&lt;p&gt;We don’t need to create a specific IZooKeeper stub object here as Rhino will do that for us and if you step through the test in debug mode you can see that we have a fake object created for us:&lt;/p&gt;&lt;p&gt;&lt;a href="http://lh4.ggpht.com/_5dD_rBQSs2o/TEz6qkNRYSI/AAAAAAAAAxc/qSGSiPg1BcQ/s1600-h/image%5B4%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh6.ggpht.com/_5dD_rBQSs2o/TEz6r2K-_OI/AAAAAAAAAxg/bgdjXZf3tfY/image_thumb%5B2%5D.png?imgmax=800" width="480" height="134" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;h3&gt;Moq&lt;/h3&gt;&lt;p&gt;The code for Moq is much the same apart from the fact that we have to set up the property behaviour for the monkey’s name and remember if we’re dealing with the mock or the object it’s returning.&lt;/p&gt;&lt;p&gt;&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:e3bfd2ee-282e-4917-b33c-c5405db3c644" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: c#;"&gt;[Fact]&lt;br /&gt;public void Moq_recursive_mocks()&lt;br /&gt;{&lt;br /&gt; var monkey = new Mock&amp;lt;IMonkey&amp;gt;();&lt;br /&gt; monkey.Setup(m =&amp;gt; m.Keeper().AssignedMonkey).Returns(monkey.Object);&lt;br /&gt; monkey.SetupProperty(m =&amp;gt; m.Name);&lt;br /&gt; monkey.Object.Name = "Spike";&lt;br /&gt; Assert.NotNull(monkey.Object.Keeper());&lt;br /&gt; Assert.Equal("Spike", monkey.Object.Keeper().AssignedMonkey.Name);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/p&gt;&lt;p&gt;Interestingly, if you look at the code in the final assert closely you’ll see that the mock/object distinction is lost when we start using the recursive mocks.&amp;#160; Instead of using Keeper().Object.AssignedMonkey we just have Keeper().AssignedMonkey.&amp;#160; It’s not a big deal, but it seems to break the paradigm that Moq has used elsewhere of separating the Mock from the object itself.&lt;/p&gt;&lt;h3&gt;NSubstitute&lt;/h3&gt;&lt;p&gt;And finally the NSubstitute approach&lt;/p&gt;&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:24b917bc-c43c-4e47-9395-8aa37d38991b" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: c#;"&gt;[Fact]&lt;br /&gt;public void Nsubstitute_recursive_mocks()&lt;br /&gt;{&lt;br /&gt; var monkey = Substitute.For&amp;lt;IMonkey&amp;gt;();&lt;br /&gt; monkey.Keeper().AssignedMonkey.Returns(monkey);&lt;br /&gt; monkey.Name = "Spike";&lt;br /&gt; Assert.NotNull(monkey.Keeper());&lt;br /&gt; Assert.Equal("Spike", monkey.Keeper().AssignedMonkey.Name);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Nice and clean.&amp;#160; No lambdas. Expressive and direct.&amp;#160; What more could you want :-)&lt;/p&gt;&lt;p&gt;P.S. At time of writing the recursive behaviour was only available when building from source.&lt;/p&gt;&lt;p&gt;&amp;#160;&lt;/p&gt;&lt;p&gt;The verdict? NSubstitute is the clear winner here.&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Other posts in this series:&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-1-basics.html"&gt;1: The Basics&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-2-properties.html"&gt;2: Properties&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-3-interactions.html"&gt;3: Interactions&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-4-parameter.html"&gt;4: Parameter Constraints&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-5-repetitions.html"&gt;5: Repetitions&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-6-multiple.html"&gt;6: Multiple Calls&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-7-exceptions.html"&gt;7: Exceptions&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-9-functions.html"&gt;9: Functions&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/08/mocking-comparison-part-10-events.html"&gt;10: Events&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/08/mocking-comparison-part-11-multiple.html"&gt;11: Multiple Interfaces&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/08/mocking-comparison-part-12-unmockables.html"&gt;12: The UnMockables&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-638366900969220881?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/638366900969220881/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2010/07/mocking-comparison-part-8-recursive.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/638366900969220881'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/638366900969220881'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2010/07/mocking-comparison-part-8-recursive.html' title='Mocking Comparison – Part 8: Recursive Mocks'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/_5dD_rBQSs2o/TEz6r2K-_OI/AAAAAAAAAxg/bgdjXZf3tfY/s72-c/image_thumb%5B2%5D.png?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-579645816141654614</id><published>2010-07-29T10:16:00.001+10:00</published><updated>2010-07-29T10:16:22.159+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='TFS'/><title type='text'>How To “Remember My Password” with TFS 2010</title><content type='html'>&lt;p&gt;If you connect to a TFS server with a machine that isn’t in the same domain then you will typically see something like this:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_5dD_rBQSs2o/TFDIK5HoCBI/AAAAAAAAAxs/YWS210yI0IY/s1600-h/image%5B3%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_5dD_rBQSs2o/TFDIMMXnDtI/AAAAAAAAAxw/r_jxO41nIAU/image_thumb%5B1%5D.png?imgmax=800" width="484" height="302" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;So you click the Use different credentials button to login and get a dialog that looks the this. But wait! Where’s the “Remember my password” checkbox?!&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_5dD_rBQSs2o/TFDIM9KLzHI/AAAAAAAAAx0/8wV0pLfDHjw/s1600-h/image%5B7%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_5dD_rBQSs2o/TFDIOmHK-yI/AAAAAAAAAx4/MMyCOqwXFqE/image_thumb%5B3%5D.png?imgmax=800" width="443" height="260" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;It’s not there!&amp;#160; It’s OK, don’t panic. There is a way to avoid entering your credentials every time you want to connect.&lt;/p&gt;  &lt;p&gt;In Windows 7 you can use the Credential Manager feature as follows.&amp;#160; Head to the control panel and click the Credential Manager icon.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_5dD_rBQSs2o/TFDIPlYdBsI/AAAAAAAAAx8/D-GKELpUC2A/s1600-h/image%5B17%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_5dD_rBQSs2o/TFDIQ9cXQgI/AAAAAAAAAyA/Tcd3TEfZDCM/image_thumb%5B7%5D.png?imgmax=800" width="421" height="339" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Next click the “Add a Windows credential” link: &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_5dD_rBQSs2o/TFDIR1AW3XI/AAAAAAAAAyI/qk7nxJOE6pc/s1600-h/image%5B18%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/_5dD_rBQSs2o/TFDITP_-NzI/AAAAAAAAAyM/eao5nGmqnJ0/image_thumb%5B8%5D.png?imgmax=800" width="463" height="299" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Then enter your details.&amp;#160; Not that the network address is just the machine name of the TFS server you are connecting to.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_5dD_rBQSs2o/TFDIT7_qlDI/AAAAAAAAAyQ/tWarbGj7uTc/s1600-h/image%5B19%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh4.ggpht.com/_5dD_rBQSs2o/TFDIVHvr6fI/AAAAAAAAAyU/_Zrx-C2mT5A/image_thumb%5B9%5D.png?imgmax=800" width="484" height="200" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;And you’re done!&amp;#160; Go back to Visual Studio, connect to TFS again and you won’t have any more login prompts.&amp;#160; Yay!&lt;/p&gt;  &lt;p&gt;For the Windows XP/Vista users you will need to go to Control Panel -&amp;gt; User Accounts -&amp;gt; Manage your Network Passwords instead.&amp;#160; The UI is a little different, but it should be fairly self-evident..&lt;/p&gt;  &lt;p&gt;P.S. For the observant amongst you wondering why my machine is TFS2008-VM when this is a post about TFS2010, it’s because I’m using an upgraded TFS2008 instance.&amp;#160; It’s definitely a TFS2010 machine :-)&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-579645816141654614?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/579645816141654614/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2010/07/how-to-remember-my-password-with-tfs.html#comment-form' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/579645816141654614'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/579645816141654614'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2010/07/how-to-remember-my-password-with-tfs.html' title='How To “Remember My Password” with TFS 2010'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/_5dD_rBQSs2o/TFDIMMXnDtI/AAAAAAAAAxw/r_jxO41nIAU/s72-c/image_thumb%5B1%5D.png?imgmax=800' height='72' width='72'/><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-8214043446228931345</id><published>2010-07-28T11:25:00.001+10:00</published><updated>2010-08-06T08:56:01.238+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mocking'/><title type='text'>Mocking Comparison – Part 7: Exceptions</title><content type='html'>&lt;p&gt;One of the great benefits of using mocks in your tests is being able to check how your classes behave when exceptions are thrown.&amp;#160; You do test for this sort of thing, right? :-)&lt;/p&gt;  &lt;p&gt;Let’s say we have a persistence mechanism that talks to SQL.&amp;#160; What happens when we get a SQL primary key violation?&amp;#160; What if we are talking to web services and the connection drops or we get some other WCF exception?&amp;#160; How do we handle that?&lt;/p&gt;  &lt;p&gt;More importantly, how do we get Rhino Mocks, Moq or NSubstitute to raise those exceptions.&amp;#160; Let’s have a look.&lt;/p&gt;  &lt;h3&gt;Rhino Mocks&lt;/h3&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:ffa92971-2ae7-4dfd-821c-de45d7bd5028" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: c#;"&gt;[Fact]&lt;br /&gt;public void Rhino_throwing_exceptions()&lt;br /&gt;{&lt;br /&gt; var monkey = MockRepository.GenerateMock&amp;lt;IMonkey&amp;gt;();&lt;br /&gt; monkey.Stub(m =&amp;gt; m.Name).Throw(new ApplicationException());&lt;br /&gt; Assert.Throws&amp;lt;ApplicationException&amp;gt;(() =&amp;gt; monkey.Name);&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is pretty simple code.&amp;#160; Instead of returning a value, just throw an exception when the method is called.&amp;#160; Too easy!&lt;/p&gt;&lt;h3&gt;Moq&lt;/h3&gt;&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:575ce605-2232-4483-a743-56b8fc04661a" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: c#;"&gt;[Fact]&lt;br /&gt;public void Moq_throwing_exceptions()&lt;br /&gt;{&lt;br /&gt; var monkey = new Mock&amp;lt;IMonkey&amp;gt;();&lt;br /&gt; monkey.Setup(m =&amp;gt; m.Name).Throws(new ApplicationException());&lt;br /&gt; Assert.Throws&amp;lt;ApplicationException&amp;gt;(() =&amp;gt; monkey.Object.Name);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The Moq code is much the same as the Rhino code, except we still have that .Object. tax that annoys me so much.&lt;/p&gt;&lt;h3&gt;NSubstitute&lt;/h3&gt;&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:0eb64a94-4ff9-4f66-a5f5-6b954063f87b" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: c#;"&gt;[Fact]&lt;br /&gt;public void Nsub_throwing_exceptions()&lt;br /&gt;{&lt;br /&gt; var monkey = Substitute.For&amp;lt;IMonkey&amp;gt;();&lt;br /&gt; monkey.Name.Returns(args =&amp;gt; { throw new ApplicationException(); });&lt;br /&gt; Assert.Throws&amp;lt;ApplicationException&amp;gt;(() =&amp;gt; monkey.Name);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Not quite as clean as the other two frameworks here, though I’m sure the guys will rectify this soon enough.&lt;/p&gt;&lt;p&gt;&amp;#160;&lt;/p&gt;&lt;p&gt;And the winner: Rhino Mocks. Even though Moq requires slightly less code than Rhino it’s .Object. distinctions will always annoy me.&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Other posts in this series:&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-1-basics.html"&gt;1: The Basics&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-2-properties.html"&gt;2: Properties&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-3-interactions.html"&gt;3: Interactions&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-4-parameter.html"&gt;4: Parameter Constraints&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-5-repetitions.html"&gt;5: Repetitions&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-6-multiple.html"&gt;6: Multiple Calls&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-8-recursive.html"&gt;8: Recursive Mocks&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-9-functions.html"&gt;9: Functions&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/08/mocking-comparison-part-10-events.html"&gt;10: Events&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/08/mocking-comparison-part-11-multiple.html"&gt;11: Multiple Interfaces&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/08/mocking-comparison-part-12-unmockables.html"&gt;12: The UnMockables&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-8214043446228931345?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/8214043446228931345/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2010/07/mocking-comparison-part-7-exceptions.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/8214043446228931345'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/8214043446228931345'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2010/07/mocking-comparison-part-7-exceptions.html' title='Mocking Comparison – Part 7: Exceptions'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-5159016763849469775</id><published>2010-07-27T13:16:00.001+10:00</published><updated>2010-07-27T13:16:13.911+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='usability'/><category scheme='http://www.blogger.com/atom/ns#' term='general'/><title type='text'>Bad Keyboard Design</title><content type='html'>&lt;p&gt;I have to wonder at times what goes through designer’s heads and wether they use their own products.&amp;#160; Have a look at the following keyboard for a HP machine I’ve been given to use when on site.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_5dD_rBQSs2o/TE5PddiiMoI/AAAAAAAAAxk/TL8V-4PRvu8/s1600-h/IMG_0089%5B4%5D.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="IMG_0089" border="0" alt="IMG_0089" src="http://lh3.ggpht.com/_5dD_rBQSs2o/TE5Pe_UR-qI/AAAAAAAAAxo/iNCY0EuHJmc/IMG_0089_thumb%5B2%5D.jpg?imgmax=800" width="484" height="364" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Look at the size of that Shift key on the left there. The thing is about half it’s normal size, which of course means I keep hitting the key next to it far too often and screwing up my typing.&amp;#160; I have to train my hands to perform more finger gymnastics than is normal, and can’t easily slide my left thumb into position like I normally would (maybe I shouldn’t use my thumb for shift, but it’s a habit I’ve picked up from gaming and it’s hard to overcome).&lt;/p&gt;  &lt;p&gt;But&amp;#160; maybe there’s a reason why they did it.&amp;#160; A reason why they cut the left shift key in half but kept the right hand one full sized?&amp;#160; Oh, there’s it is! It’s so they could add an extra (and duplicated) key on the keyboard! I for one know that the backslash and pipe characters are easily the most commonly used ones in my arsenal.&amp;#160; On my laptop the backslash key is so worn away from use I can hardly make out the symbols on it any more.&amp;#160; It’s worse than even the ‘e’ and return keys!&amp;#160; Thank goodness keyboard usability was sacrificed to add an extra one of those puppies!&amp;#160; Thank you HP! Thank you!&lt;/p&gt;  &lt;p&gt;OK. Rant over.&amp;#160; Time to go steal a keyboard from someone else in the office, and while I do that why don’t you let me know what the most annoying keyboard design is that you’ve used or seen?&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-5159016763849469775?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/5159016763849469775/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2010/07/bad-keyboard-design.html#comment-form' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/5159016763849469775'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/5159016763849469775'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2010/07/bad-keyboard-design.html' title='Bad Keyboard Design'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/_5dD_rBQSs2o/TE5Pe_UR-qI/AAAAAAAAAxo/iNCY0EuHJmc/s72-c/IMG_0089_thumb%5B2%5D.jpg?imgmax=800' height='72' width='72'/><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-1209947817875887764</id><published>2010-07-26T14:54:00.001+10:00</published><updated>2010-08-06T08:55:41.052+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mocking'/><title type='text'>Mocking Comparison – Part 6: Multiple Calls</title><content type='html'>&lt;p&gt;Our comparison of Rhino Mocks, Moq and NSubstitute continues with a look at how multiple calls to a mock are handled and what you do if you want to alter the return values on subsequent calls.&lt;/p&gt;  &lt;p&gt;Consider the scenario where you have a method you’re calling that you want to be successful the first time you call it, but where subsequent calls should fail (such as trying to save the same data twice, etc).&amp;#160; How do you do this?&lt;/p&gt;  &lt;h3&gt;Rhino Mocks&lt;/h3&gt;  &lt;p&gt;In Rhino Mocks to set return values for multiple calls to a method you simply specify the return value that many times.&amp;#160; To avoid duplication of code you can use the repetition syntax we saw in &lt;a title="Repetition Syntax" href="http://www.richard-banks.org/2010/07/mocking-comparison-part-5-repetitions.html" target="_blank"&gt;Part 5&lt;/a&gt;.&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:f9e12d94-b560-41d6-9f31-b2908a3a8e32" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: c#;"&gt;[Fact]&lt;br /&gt;public void Rhino_multiple_calls()&lt;br /&gt;{&lt;br /&gt; var monkey = MockRepository.GenerateMock&amp;lt;IMonkey&amp;gt;();&lt;br /&gt;&lt;br /&gt; monkey.Stub(m =&amp;gt; m.TryAddFleas(1)).Return(true).Repeat.Twice();&lt;br /&gt; monkey.Stub(m =&amp;gt; m.TryAddFleas(1)).Return(false);&lt;br /&gt;&lt;br /&gt; Assert.True(monkey.TryAddFleas(1));&lt;br /&gt; Assert.True(monkey.TryAddFleas(1));&lt;br /&gt; Assert.False(monkey.TryAddFleas(1));&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Given that we have set up the return value 3 times, what happens when we make the call a fourth time?&amp;#160; At this point Rhino Mocks, having exhausted it’s known set of return values, will start returning the default value.&lt;/p&gt;&lt;p&gt;What if you just want to return the value True for every call?&amp;#160; We would use the Repeat.Any() method as shown here:&lt;/p&gt;&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:4df2220f-1f57-444a-aba8-cf3ba174a480" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: c#;"&gt;[Fact]&lt;br /&gt;public void Rhino_multiple_calls()&lt;br /&gt;{&lt;br /&gt; var monkey = MockRepository.GenerateMock&amp;lt;IMonkey&amp;gt;();&lt;br /&gt;&lt;br /&gt; monkey.Stub(m =&amp;gt; m.TryAddFleas(1)).Return(true).Repeat.Any();&lt;br /&gt;&lt;br /&gt; Assert.True(monkey.TryAddFleas(1));&lt;br /&gt; Assert.True(monkey.TryAddFleas(1));&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;h3&gt;Moq&lt;/h3&gt;&lt;p&gt;Moq doesn’t have a nice way of defining mixed return values (not that I can find anyway) however you can implement the same thing using callbacks, which we’ll cover in a later post.&lt;/p&gt;&lt;p&gt;By default setting a return value on a method call will always return that value no matter how many times it is called, so we can at least do the following:&lt;/p&gt;&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:915c60f5-6144-4906-b1d3-48b5f53ee898" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: c#;"&gt;[Fact]&lt;br /&gt;public void Moq_multiple_calls()&lt;br /&gt;{&lt;br /&gt; var monkey = new Mock&amp;lt;IMonkey&amp;gt;();&lt;br /&gt; monkey.Setup(m =&amp;gt; m.TryAddFleas(1)).Returns(true);&lt;br /&gt;&lt;br /&gt; Assert.True(monkey.Object.TryAddFleas(1));&lt;br /&gt; Assert.True(monkey.Object.TryAddFleas(1));&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;h3&gt;NSubstitute&lt;/h3&gt;&lt;p&gt;The code for mixed return values in NSubstitute is so nice.&amp;#160; To handle multiple calls providing different return values you can just provide multiple values or use an array in the return statement as follows:&lt;/p&gt;&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:1ba69e4e-efd6-49e0-acaa-452939161757" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: c#;"&gt;[Fact]&lt;br /&gt;public void Nsubstitute_multiple_calls()&lt;br /&gt;{&lt;br /&gt; var monkey = Substitute.For&amp;lt;IMonkey&amp;gt;();&lt;br /&gt;&lt;br /&gt; monkey.TryAddFleas(1).Returns(true,true,false);&lt;br /&gt;&lt;br /&gt; Assert.True(monkey.TryAddFleas(1));&lt;br /&gt; Assert.True(monkey.TryAddFleas(1));&lt;br /&gt; Assert.False(monkey.TryAddFleas(1));&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And if you just want to always return true, then it works just like Moq does (with the benefit of not requiring the .Object. tax in the assert statements:&lt;/p&gt;&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:a9a7971f-f5dc-4f4f-ae52-6233f807b418" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: c#;"&gt;[Fact]&lt;br /&gt;public void Nsubstitute_multiple_calls()&lt;br /&gt;{&lt;br /&gt; var monkey = Substitute.For&amp;lt;IMonkey&amp;gt;();&lt;br /&gt;&lt;br /&gt; monkey.TryAddFleas(1).Returns(true);&lt;br /&gt;&lt;br /&gt; Assert.True(monkey.TryAddFleas(1));&lt;br /&gt; Assert.True(monkey.TryAddFleas(1));&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you can’t tell, my decision on the best framework to choose goes to NSubstitute.&amp;#160; It has such a simple and elegant way to handle both requirements, and again, not a single lambda in site.&amp;#160; Excellent!&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Other posts in this series:&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-1-basics.html"&gt;1: The Basics&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-2-properties.html"&gt;2: Properties&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-3-interactions.html"&gt;3: Interactions&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-4-parameter.html"&gt;4: Parameter Constraints&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-5-repetitions.html"&gt;5: Repetitions&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-7-exceptions.html"&gt;7: Exceptions&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-8-recursive.html"&gt;8: Recursive Mocks&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-9-functions.html"&gt;9: Functions&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/08/mocking-comparison-part-10-events.html"&gt;10: Events&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/08/mocking-comparison-part-11-multiple.html"&gt;11: Multiple Interfaces&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/08/mocking-comparison-part-12-unmockables.html"&gt;12: The UnMockables&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-1209947817875887764?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/1209947817875887764/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2010/07/mocking-comparison-part-6-multiple.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/1209947817875887764'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/1209947817875887764'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2010/07/mocking-comparison-part-6-multiple.html' title='Mocking Comparison – Part 6: Multiple Calls'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-3901415120483279569</id><published>2010-07-23T13:23:00.002+10:00</published><updated>2010-08-06T08:55:14.565+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mocking'/><title type='text'>Mocking Comparison - Part 5: Repetitions</title><content type='html'>&lt;p&gt;In &lt;a title="Parameter Constraints" href="http://www.richard-banks.org/2010/07/mocking-comparison-part-4-parameter.html" target="_blank"&gt;Part 4&lt;/a&gt; we looked at how parameter constraints are handled and in &lt;a title="Interactions" href="http://www.richard-banks.org/2010/07/mocking-comparison-part-3-interactions.html" target="_blank"&gt;Part 3&lt;/a&gt; we looked at how to do interaction based tests with mocks.&amp;#160; In this part we bring those two pieces together and add a little extra to check if a call was made a specific number of times.&lt;/p&gt;  &lt;p&gt;Let’s just jump straight into the code shall we?&lt;/p&gt;  &lt;h3&gt;Rhino Mocks&lt;/h3&gt;  &lt;p&gt;Our test here is simply going to call a method a number of times and verify that the call was made the correct number of times using constraints to verify the correct calls.&amp;#160; It’s a completely useless test, other than as a vehicle to show you how to do this sort of thing&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:b9618701-6388-48c5-a4c1-4f8bd35d2550" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: c#;"&gt;[Fact]&lt;br /&gt;public void Rhino_repetitions()&lt;br /&gt;{&lt;br /&gt; var monkey = MockRepository.GenerateMock&amp;lt;IMonkey&amp;gt;();&lt;br /&gt;&lt;br /&gt; monkey.TryAddFleas(5);&lt;br /&gt; monkey.TryAddFleas(-1);&lt;br /&gt; monkey.TryAddFleas(9);&lt;br /&gt;&lt;br /&gt; monkey.AssertWasCalled(m =&amp;gt; m.TryAddFleas(0),&lt;br /&gt;  options =&amp;gt; options.Constraints(&lt;br /&gt;   Is.GreaterThan(3) &amp;amp;&amp;amp; Is.LessThanOrEqual(10)&lt;br /&gt;  )&lt;br /&gt;  .Repeat.Twice());&lt;br /&gt; monkey.AssertWasCalled(m =&amp;gt; m.TryAddFleas(-1),&lt;br /&gt;  options =&amp;gt; options.Repeat.Once());&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note the important part, the .Repeat.Twice() and .Repeat.Once() calls.&amp;#160; It’s these calls that define our expectations as to how many times the call should have been made.&lt;/p&gt;&lt;p&gt;Rhino also features a .Repeat.Time(n) call you can use as well if once or twice don’t cut it for you.&lt;/p&gt;&lt;h3&gt;Moq&lt;/h3&gt;&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:0b6c9fa2-e577-4261-bd1e-cb122842b6f9" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: c#;"&gt;[Fact]&lt;br /&gt;public void Moq_repetitions()&lt;br /&gt;{&lt;br /&gt; var monkey = new Mock&amp;lt;IMonkey&amp;gt;();&lt;br /&gt;&lt;br /&gt; monkey.Object.TryAddFleas(5);&lt;br /&gt; monkey.Object.TryAddFleas(-1);&lt;br /&gt; monkey.Object.TryAddFleas(9);&lt;br /&gt;&lt;br /&gt; monkey.Verify(m =&amp;gt; m.TryAddFleas(&lt;br /&gt;   It.IsInRange(3,10,Range.Exclusive)&lt;br /&gt;  ),&lt;br /&gt;  Times.Exactly(2));&lt;br /&gt; monkey.Verify(m =&amp;gt; m.TryAddFleas(-1), Times.Once());&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Instead of using the word Repeat, Moq uses Times.&amp;#160; Apart from that there is little difference.&lt;/p&gt;&lt;h3&gt;NSubstitute&lt;/h3&gt;&lt;p&gt;Unfortunately NSubstitute doesn’t support this feature yet as it’s still a maturing framework.&amp;#160; If you really need to do this type of testing then you’ve got a few options – use Rhino or Moq, contribute to the NSubstitute project, or to just not do this type of testing :-).&lt;/p&gt;&lt;p&gt;Interaction based testing is valid at times, but it’s generally brittle and is usually a sign of “implementation verifying” tests rather than behaviour/specification verifying tests (but that’s an argument for another time)&lt;/p&gt;&lt;p&gt;&amp;#160;&lt;/p&gt;&lt;p&gt;Verdict: Moq wins out in this case simply because it’s constraint system is more terse than the Rhino one, but this is purely a personal taste thing.&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Other posts in this series:&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-1-basics.html"&gt;1: The Basics&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-2-properties.html"&gt;2: Properties&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-3-interactions.html"&gt;3: Interactions&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-4-parameter.html"&gt;4: Parameter Constraints&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-6-multiple.html"&gt;6: Multiple Calls&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-7-exceptions.html"&gt;7: Exceptions&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-8-recursive.html"&gt;8: Recursive Mocks&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-9-functions.html"&gt;9: Functions&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/08/mocking-comparison-part-10-events.html"&gt;10: Events&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/08/mocking-comparison-part-11-multiple.html"&gt;11: Multiple Interfaces&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/08/mocking-comparison-part-12-unmockables.html"&gt;12: The UnMockables&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-3901415120483279569?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/3901415120483279569/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2010/07/mocking-comparison-part-5-repetitions.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/3901415120483279569'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/3901415120483279569'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2010/07/mocking-comparison-part-5-repetitions.html' title='Mocking Comparison - Part 5: Repetitions'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-1223175520250091180</id><published>2010-07-22T16:45:00.001+10:00</published><updated>2010-08-06T08:54:54.126+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mocking'/><title type='text'>Mocking Comparison – Part 4: Parameter Constraints</title><content type='html'>&lt;p&gt;Continuing with our comparison of Rhino Mocks, Moq and NSubstitute we now turn our eye to how constraints are managed.&lt;/p&gt;  &lt;p&gt;Consider a method with parameters that was want to mock.&amp;#160; By default, a mock object will only return a specified value if the parameters passed to it exactly match the call signature specified to the mock object.&amp;#160; Maybe that’s a bit wordy, so as a quick example if you say Method(“a”).Returns(“Fred”) you’ll only get “Fred” back from the mock when you pass “a” as the parameter.&amp;#160; Passing “b” gets you nothing. Make sense?&lt;/p&gt;  &lt;p&gt;OK, so what if I then want my mock object to provide different return values when different parameter values are used.&amp;#160; How do we avoid writing too much code or having to work out exactly what we should expect as a parameter every time?&amp;#160; And what if I don’t care what’s passed in, I just want a return value.&lt;/p&gt;  &lt;p&gt;This is where constraints come into play.&amp;#160; Let’s look at some code to show how it works:&lt;/p&gt;  &lt;h3&gt;Rhino Mocks&lt;/h3&gt;  &lt;p&gt;Here’s a test where we try to add fleas to our monkey.&amp;#160; Adding positive numbers of fleas should succeed and adding a negative number of fleas should fail.&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:77ae5b8e-5f92-442e-ae7a-8ad509c9137b" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: c#;"&gt;[Fact]&lt;br /&gt;public void Rhino_no_constraints()&lt;br /&gt;{&lt;br /&gt; var monkey = MockRepository.GenerateMock&amp;lt;IMonkey&amp;gt;();&lt;br /&gt; monkey.Stub(m =&amp;gt; m.TryAddFleas(5)).Return(true);&lt;br /&gt; monkey.Stub(m =&amp;gt; m.TryAddFleas(-1)).Return(false);&lt;br /&gt; monkey.Stub(m =&amp;gt; m.TryAddFleas(9)).Return(true);&lt;br /&gt;&lt;br /&gt; Assert.Equal(true, monkey.TryAddFleas(5));&lt;br /&gt; Assert.Equal(false, monkey.TryAddFleas(-1));&lt;br /&gt; Assert.Equal(true, monkey.TryAddFleas(9));&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Notice how the use of explicit parameter values in the stubs means we have to repeat a lot of code.&amp;#160; Hmm, that’s annoying.&lt;/p&gt;&lt;p&gt;Thankfully we can simplify this by just ignoring the arguments as seen in the code below.&amp;#160; Note that we still have to supply a parameter value for the TryAddFleas lambda in the Stub call, even though it’s ignored.&lt;/p&gt;&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:ab477b0c-7142-44d3-bf50-a3bfbc180ce2" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: c#;"&gt;[Fact]&lt;br /&gt;public void Rhino_ignore_arguments()&lt;br /&gt;{&lt;br /&gt; var monkey = MockRepository.GenerateMock&amp;lt;IMonkey&amp;gt;();&lt;br /&gt; monkey.Stub(m =&amp;gt; m.TryAddFleas(0)).IgnoreArguments().Return(true);&lt;br /&gt;&lt;br /&gt; Assert.Equal(true, monkey.TryAddFleas(5));&lt;br /&gt; Assert.Equal(false, monkey.TryAddFleas(-1));&lt;br /&gt; Assert.Equal(true, monkey.TryAddFleas(9));&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;However this will now cause the test to fail because we no longer return a false when passed -1 as a value.&lt;/p&gt;&lt;p&gt;So what we really want is to only return true when the argument value is in a certain range.&amp;#160; For fun let’s make that range between 4 and 9 inclusive.&amp;#160; Here’s the test now, using constraints:&lt;/p&gt;&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:8913c4e6-9cee-48c7-ada5-b90683d33fd2" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: c#;"&gt;[Fact]&lt;br /&gt;public void Rhino_constraints()&lt;br /&gt;{&lt;br /&gt; var monkey = MockRepository.GenerateMock&amp;lt;IMonkey&amp;gt;();&lt;br /&gt;&lt;br /&gt; monkey.Stub(m =&amp;gt; m.TryAddFleas(0))&lt;br /&gt;  .Constraints(Is.LessThan(10) &amp;amp;&amp;amp; Is.GreaterThan(3))&lt;br /&gt;  .Return(true);&lt;br /&gt; Assert.Equal(true, monkey.TryAddFleas(5));&lt;br /&gt; Assert.Equal(false, monkey.TryAddFleas(-1));&lt;br /&gt; Assert.Equal(true, monkey.TryAddFleas(9));&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Whilst this means I have 2 less Stub calls to make, it does tend to be a little verbose.&amp;#160; Expressive, but verbose.&amp;#160; For those wondering why I don’t have a constraint for the -1 argument, I’m relying on the standard behaviour of mocks.&amp;#160; If a constraint isn’t matched then the standard mock behaviour is to return the default value of the return type, being false in this case.&lt;/p&gt;&lt;h3&gt;Moq&lt;/h3&gt;&lt;p&gt;Here’s the same thing in Moq.&amp;#160; For brevity and completeness I’ve included a commented out line that shows what you would do if you just wanted to ignore parameter values&lt;/p&gt;&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:51a29e56-920e-48e2-a588-adb512315132" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: c#;"&gt;[Fact]&lt;br /&gt;public void Moq_constraints()&lt;br /&gt;{&lt;br /&gt; var monkey = new Mock&amp;lt;IMonkey&amp;gt;();&lt;br /&gt;&lt;br /&gt; //monkey.Setup(m =&amp;gt; m.TryAddFleas(It.IsAny&amp;lt;int&amp;gt;())).Returns(true);&lt;br /&gt; monkey.Setup(m =&amp;gt; m.TryAddFleas(It.IsInRange(3, 10, Range.Exclusive)))&lt;br /&gt;  .Returns(true);&lt;br /&gt; Assert.Equal(true, monkey.Object.TryAddFleas(5));&lt;br /&gt; Assert.Equal(false, monkey.Object.TryAddFleas(-1));&lt;br /&gt; Assert.Equal(true, monkey.Object.TryAddFleas(9));&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The IsInRange method is quite nice, and the range constraints can be either inclusive or exclusive, however the code feels as verbose if not more so than the Rhino approach.&lt;/p&gt;&lt;p&gt;Note that if you use a test harness like MSpec (Machine.Specifications) then the “It” static class that Moq uses clashes with the “It” class used by MSpec for defining specifications which makes writing code a little painful at times.&lt;/p&gt;&lt;h3&gt;NSubstitute&lt;/h3&gt;&lt;p&gt;Finally the NSubstitute version.&lt;/p&gt;&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:60266510-d85f-4174-9663-15f94ce0ac57" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: c#;"&gt;[Fact]&lt;br /&gt;public void Nsubtitute_constraints()&lt;br /&gt;{&lt;br /&gt; var monkey = Substitute.For&amp;lt;IMonkey&amp;gt;();&lt;br /&gt;&lt;br /&gt; //monkey.TryAddFleas(Arg.Any&amp;lt;int&amp;gt;()).Returns(true);&lt;br /&gt; monkey.TryAddFleas(Arg.Is&amp;lt;int&amp;gt;(count =&amp;gt; count &amp;gt; 3 &amp;amp;&amp;amp; count &amp;lt; 10))&lt;br /&gt;  .Returns(true);&lt;br /&gt;&lt;br /&gt; Assert.Equal(true, monkey.TryAddFleas(5));&lt;br /&gt; Assert.Equal(false, monkey.TryAddFleas(-1));&lt;br /&gt; Assert.Equal(true, monkey.TryAddFleas(9));&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This works much the same as the others with the difference being that you have a predicate as the parameter and you need to supply the parameter type to the Arg.Is&amp;lt;T&amp;gt; call.&lt;/p&gt;&lt;p&gt;From a readability perspective, there’s less code which is good and it’s easier to read than the other frameworks because of it’s terseness.&amp;#160; As a bonus, using the Arg.Any&amp;lt;T&amp;gt; call means we can avoid lambdas completely..&lt;/p&gt;&lt;p&gt;&amp;#160;&lt;/p&gt;&lt;p&gt;Verdict: I’ll go with the NSubstitute version as my first choice, though it’s more a matter of style choice than anything else. After that I prefer the Moq syntax over Rhino in this case.&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Other posts in this series:&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-1-basics.html"&gt;1: The Basics&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-2-properties.html"&gt;2: Properties&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-3-interactions.html"&gt;3: Interactions&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-5-repetitions.html"&gt;5: Repetitions&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-6-multiple.html"&gt;6: Multiple Calls&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-7-exceptions.html"&gt;7: Exceptions&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-8-recursive.html"&gt;8: Recursive Mocks&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-9-functions.html"&gt;9: Functions&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/08/mocking-comparison-part-10-events.html"&gt;10: Events&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/08/mocking-comparison-part-11-multiple.html"&gt;11: Multiple Interfaces&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/08/mocking-comparison-part-12-unmockables.html"&gt;12: The UnMockables&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-1223175520250091180?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/1223175520250091180/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2010/07/mocking-comparison-part-4-parameter.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/1223175520250091180'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/1223175520250091180'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2010/07/mocking-comparison-part-4-parameter.html' title='Mocking Comparison – Part 4: Parameter Constraints'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-2640679655088288230</id><published>2010-07-21T21:37:00.001+10:00</published><updated>2010-08-06T08:54:27.450+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mocking'/><title type='text'>Mocking Comparison – Part 3: Interactions</title><content type='html'>&lt;p&gt;Our comparison of Rhino Mocks, Moq and NSubstitute continues with a look at how these frameworks support interaction based testing (whether that’s a good idea or not is not going to be dealt with here!).&lt;/p&gt;  &lt;p&gt;The idea behind interaction based testing is checking if our class under test makes appropriate calls to the mocked objects.&amp;#160; Typically this is done when testing against API’s that want things done in a certain order or where you want to check that the Save method of a repository was called, as an example.&lt;/p&gt;  &lt;p&gt;For our example code we’re going to be testing that the Keeper makes the appropriate call to check the current flea count and then, because the flea count is low, does not try and clean the monkey, i.e. Monkey.Clean() should not be called.&lt;/p&gt;  &lt;p&gt;Note that code for the classes under test is in &lt;a title="Mocking Comparison - Part 1" href="http://www.richard-banks.org/2010/07/mocking-comparison-part-1-basics.html" target="_blank"&gt;Part 1&lt;/a&gt; of this series.&lt;/p&gt;  &lt;h3&gt;Rhino Mocks&lt;/h3&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:b83a9ae1-110c-4885-ae88-7964d842fadc" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: c#;"&gt;[Fact]&lt;br /&gt;public void Rhino_method_was_called()&lt;br /&gt;{&lt;br /&gt; var monkey = MockRepository.GenerateMock&amp;lt;IMonkey&amp;gt;();&lt;br /&gt; var keeper = new ZooKeeper {AssignedMonkey = monkey};&lt;br /&gt; monkey.Stub(m =&amp;gt; m.CurrentFleaCount()).Return(0);&lt;br /&gt;&lt;br /&gt; keeper.CleanMonkey();&lt;br /&gt;&lt;br /&gt; monkey.AssertWasCalled(m =&amp;gt; m.CurrentFleaCount());&lt;br /&gt; monkey.AssertWasNotCalled(m =&amp;gt; m.Clean());&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So in the initial arrange section of our test we stub out the CurrentFleaCount() call to return zero fleas when called.&lt;/p&gt;&lt;p&gt;We then perform the Act part of our test and ask the Keeper to clean the monkey.&lt;/p&gt;&lt;p&gt;Finally in the Assert part of our test we check if the monkey had methods called on it – the CurrentFleaCount method should be called, and the Clean method should not be called.&lt;/p&gt;&lt;p&gt;If you’re playing along at home, you may have noticed that changing the monkey’s flea count to 100 still makes the test pass, when we should have expected the test to fail (because the clean method should now be called).&amp;#160; This won’t happen because there is a second guard clause in the keeper’s CleanMonkey() method that checks if the monkey is awake and this will always return false.&lt;/p&gt;&lt;p&gt;It brings up an important point with all mock objects in that the default response for any method not explicitly stubbed out is to return the default value, and the default for a bool is false.&lt;/p&gt;&lt;h3&gt;Moq&lt;/h3&gt;&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:a2ae7fa2-a888-4eac-82a1-84b07377c0e5" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: c#;"&gt;[Fact]&lt;br /&gt;public void Moq_method_was_called()&lt;br /&gt;{&lt;br /&gt; var monkey = new Mock&amp;lt;IMonkey&amp;gt;();&lt;br /&gt; var keeper = new ZooKeeper() {AssignedMonkey = monkey.Object};&lt;br /&gt;&lt;br /&gt; monkey.Setup(m =&amp;gt; m.CurrentFleaCount()).Returns(0);&lt;br /&gt;&lt;br /&gt; keeper.CleanMonkey();&lt;br /&gt;&lt;br /&gt; monkey.Verify(m =&amp;gt; m.CurrentFleaCount());&lt;br /&gt; monkey.Verify(m =&amp;gt; m.Clean(), Times.Never());&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;  &lt;p&gt;The code here is similar to Rhino’s code however you’ll notice that to configure our mock object in Moq we use Setup instead of Stub/Expect calls to set the return behaviour for the flea count method.&lt;/p&gt;  &lt;p&gt;Moq uses a single Verify method to check that a call was made, so to check that something wasn’t called you have to check that it was called zero times – expressed in Moq syntax as Times.Never().&amp;#160; Make your own mind up over whether this is clear enough from a readability perspective, but I find that it feels a little like Yoda has been writing code and the Verify call seems like it should have a boolean statement for its parameter, not be asserting that a method was called.&lt;/p&gt;  &lt;h3&gt;NSubstitute&lt;/h3&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:314e58f2-06e8-4bc1-bab2-bfbaa827cfee" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: c#;"&gt;[Fact]&lt;br /&gt;public void Nsubstitute_method_was_called()&lt;br /&gt;{&lt;br /&gt; var monkey = Substitute.For&amp;lt;IMonkey&amp;gt;();&lt;br /&gt; var keeper = new ZooKeeper { AssignedMonkey = monkey };&lt;br /&gt; monkey.CurrentFleaCount().Returns(0);&lt;br /&gt;&lt;br /&gt; keeper.CleanMonkey();&lt;br /&gt;&lt;br /&gt; monkey.Received().CurrentFleaCount();&lt;br /&gt; monkey.DidNotReceive().Clean();&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;  &lt;p&gt;Firstly, not the lack of lambda methods anywhere in this code.&amp;#160; The mock object behaviour is setup by simply attaching a .Returns(0) to the monkey.CurrentFleaCount() method making the code more expressive and simpler to read.&lt;/p&gt;  &lt;p&gt;As far as the asserts are concerned we check that a call was Received() by our mock object or that the mock DidNotReceive() the call.&amp;#160; I find this syntax better than the other frameworks in that there’s no lambdas, but it doesn’t read quite as well as I might like.&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;The verdict: It’s a split decision between Rhino and NSubstitute, with me wanting the best of both.&lt;/p&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Other posts in this series:&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-1-basics.html"&gt;1: The Basics&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-2-properties.html"&gt;2: Properties&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-4-parameter.html"&gt;4: Parameter Constraints&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-5-repetitions.html"&gt;5: Repetitions&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-6-multiple.html"&gt;6: Multiple Calls&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-7-exceptions.html"&gt;7: Exceptions&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-8-recursive.html"&gt;8: Recursive Mocks&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-9-functions.html"&gt;9: Functions&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/08/mocking-comparison-part-10-events.html"&gt;10: Events&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/08/mocking-comparison-part-11-multiple.html"&gt;11: Multiple Interfaces&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/08/mocking-comparison-part-12-unmockables.html"&gt;12: The UnMockables&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-2640679655088288230?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/2640679655088288230/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2010/07/mocking-comparison-part-3-interactions.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/2640679655088288230'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/2640679655088288230'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2010/07/mocking-comparison-part-3-interactions.html' title='Mocking Comparison – Part 3: Interactions'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-7260881324713302006</id><published>2010-07-20T17:51:00.001+10:00</published><updated>2010-08-06T08:54:03.830+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mocking'/><title type='text'>Mocking Comparison – Part 2: Properties</title><content type='html'>&lt;p&gt;Carrying on with our Rhino Mocks, Moq and NSubstitute comparison, let’s now look at how the various frameworks handle properties on their mock objects.&lt;/p&gt;  &lt;h3&gt;Rhino Mocks&lt;/h3&gt;  &lt;p&gt;So in RhinoMocks you have two choices for creating mock objects – using GenerateMock&amp;lt;T&amp;gt;() or GenerateStub&amp;lt;T&amp;gt;().&amp;#160; GenerateMock will create a mock object that tracks calls made to it so that you can assert expectations against which calls were made at the end of your test.&amp;#160; GenerateStub on the other hand creates a stub object, which is to all intents and purposes the same as a mock object just without any of the overhead for tracking which calls were made against it.&amp;#160; In other words you can do asserts to check if a call was made on a mock, but not on a stub.&lt;/p&gt;  &lt;p&gt;Why does this matter?&amp;#160; Because properties on mocks and stubs have different default behaviours in RhinoMocks.&lt;/p&gt;  &lt;p&gt;For a mock object we have to explicitly indicate if we want a property to have a standard getter/setter applied to it (i.e. just like an autoproperty implementation).&amp;#160; Here’s some code&lt;/p&gt;  &lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:4a7b31b3-3d89-4d7b-9602-98283b84fa86" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: c#;"&gt;[Fact]&lt;br /&gt;public void Rhino_properties()&lt;br /&gt;{&lt;br /&gt; var monkey = MockRepository.GenerateMock&amp;lt;IMonkey&amp;gt;();&lt;br /&gt; monkey.Stub(m =&amp;gt; m.Name).PropertyBehavior();&lt;br /&gt;&lt;br /&gt; monkey.Name = "Spike";&lt;br /&gt; Assert.Equal("Spike", monkey.Name);&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;and the same test with a stub&lt;/p&gt;&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:2665476f-7488-40f0-872e-8146250c54ba" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp;"&gt;[Fact]&lt;br /&gt;public void Rhino_properties()&lt;br /&gt;{&lt;br /&gt; var monkey = MockRepository.GenerateStub&amp;lt;IMonkey&amp;gt;();&lt;br /&gt;&lt;br /&gt; monkey.Name = "Spike";&lt;br /&gt; Assert.Equal("Spike", monkey.Name);&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note that the stubbed object requires none of the property behaviour setup and so it feels more natural.&amp;#160; While some people may prefer to see properties being set up explicitly rather than implicitly I think that if it is a well known behaviour then it’s not going to be a problem that the property behaviour is implicitly provided on the stubbed objects.&lt;/p&gt;&lt;h3&gt;Moq&lt;/h3&gt;&lt;p&gt;The Moq code is much the same, however as you can see we have to pay the Moq .Object. tax on our calls.&lt;/p&gt;&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:f3ca25c1-73e0-4d90-b5d8-bcdf652643f4" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: csharp;"&gt;[Fact]&lt;br /&gt;public void Moq_properties()&lt;br /&gt;{&lt;br /&gt; var monkey = new Mock&amp;lt;IMonkey&amp;gt;();&lt;br /&gt; monkey.SetupProperty(m =&amp;gt; m.Name);&lt;br /&gt;&lt;br /&gt; monkey.Object.Name = "Spike";&lt;br /&gt; Assert.Equal("Spike", monkey.Object.Name);&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This makes the code feel just a little more verbose and cumbersome than the Rhino and NSubstitute versions.&lt;/p&gt;&lt;p&gt;In fairness, if you want all properties to be automatically set to the standard autoproperty behaviour for a mock object you can use the SetupAllProperties method in Moq as follows:&lt;/p&gt;&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:4d837ffe-3ae2-48eb-b746-c3fbb2e9dea5" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: c#;"&gt;[Fact]&lt;br /&gt;public void Moq_properties()&lt;br /&gt;{&lt;br /&gt; var monkey = new Mock&amp;lt;IMonkey&amp;gt;();&lt;br /&gt; monkey.SetupAllProperties();&lt;br /&gt;&lt;br /&gt; monkey.Object.Name = "Spike";&lt;br /&gt; Assert.Equal("Spike", monkey.Object.Name);&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;h3&gt;NSubstitute&lt;/h3&gt;&lt;p&gt;The NSubstitute code shown below is much the same as the Rhino Mocks code when using stubs.&amp;#160; The difference being that NSubstitute doesn’t differentiate between stubs and mocks.&lt;/p&gt;&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:f32c3428-b7e9-4f15-a8ea-c502c7ff2e88:c053b360-f06a-4e3d-a312-be2122995061" class="wlWriterEditableSmartContent"&gt;&lt;pre class="brush: c#;"&gt;[Fact]&lt;br /&gt;public void NSubstitute_properties()&lt;br /&gt;{&lt;br /&gt; var monkey = Substitute.For&amp;lt;IMonkey&amp;gt;();&lt;br /&gt;   &lt;br /&gt; monkey.Name = "Spike";&lt;br /&gt; Assert.Equal("Spike", monkey.Name);&lt;br /&gt;}&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Overall, Rhino and NSubstitute are on par, however the more expressive mock object creation and reduced confusion over whether to use mocks or stubs in NSubstitute tilts the balance in favour of NSubstitute.&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;Other posts in this series:&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-1-basics.html"&gt;1: The Basics&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-3-interactions.html"&gt;3: Interactions&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-4-parameter.html"&gt;4: Parameter Constraints&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-5-repetitions.html"&gt;5: Repetitions&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-6-multiple.html"&gt;6: Multiple Calls&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-7-exceptions.html"&gt;7: Exceptions&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-8-recursive.html"&gt;8: Recursive Mocks&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/07/mocking-comparison-part-9-functions.html"&gt;9: Functions&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/08/mocking-comparison-part-10-events.html"&gt;10: Events&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/08/mocking-comparison-part-11-multiple.html"&gt;11: Multiple Interfaces&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.richard-banks.org/2010/08/mocking-comparison-part-12-unmockables.html"&gt;12: The UnMockables&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/13321238-7260881324713302006?l=www.richard-banks.org' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.richard-banks.org/feeds/7260881324713302006/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.richard-banks.org/2010/07/mocking-comparison-part-2-properties.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/7260881324713302006'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/13321238/posts/default/7260881324713302006'/><link rel='alternate' type='text/html' href='http://www.richard-banks.org/2010/07/mocking-comparison-part-2-properties.html' title='Mocking Comparison – Part 2: Properties'/><author><name>Richard Banks</name><uri>http://www.blogger.com/profile/11682500243311050542</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://farm1.static.flickr.com/75/buddyicons/83433068@N00.jpg?1187650471'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-13321238.post-6463074561001440735</id><published>2010-07-20T00:38:00.003+10:00</published><updated>2010-08-06T08:53:32.996+10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mocking'/><title type='text'>Mocking Comparison – Part 1: The Basics</title><content type='html'>&lt;p&gt;I recently gave a talk on mocking at the &lt;a title="DDD Sydney Web Site" href="http://www.dddsydney.com" target="_blank"&gt;DDD Sydney&lt;/a&gt; conference called “You Look Like A Monkey and You Smell Like One Too”.&amp;#160; In it I not only got to call the audience various names and get mocked in return, but I also showed a number of features of mock frameworks and how they can be used.&amp;#160; Essentially it was a comparison of a number of mock frameworks, with a particular focus on a few of the main open source ones out there and a great new comer – specifically &lt;a href="http://www.ayende.com/projects/rhino-mocks.aspx" target="_blank"&gt;RhinoMocks&lt;/a&gt;, &lt;a href="http://code.google.com/p/moq/" target="_blank"&gt;Moq&lt;/a&gt; and &lt;a href="http://nsubstitute.github.com/" target="_blank"&gt;NSubstitute&lt;/a&gt;.&lt;/p&gt;  &lt;p&gt;This is part 1 of series running through the code I had on screen and providing some thoughts on the pros and cons of each framework in the context of the scenario I’m showing.&amp;#160; This first part will be longer than the others simply because we have to put some ground work in place.&amp;#160; So, let’s stop wasting time and get started…&lt;/p&gt;  &lt;h3&gt;What Are We Testing?&lt;/h3&gt;  &lt;p&gt;All right, so in order to do any mocking we really should have an application to test.&amp;#160; For the purposes of this series the code is a simple class library.&amp;#160; It does nothing useful in the real world, but it does give us something to test.&amp;#160; It’s a pseudo “zoo” featuring monkeys, zoo keepers and tourists.&lt;/p&gt;  &lt;h4&gt;The IMonkey Interface&lt;/h4&gt;  &lt;p&gt;Every zoo needs a monkey, and thus we need a monkey right? Of course we do! However, because the monkeys haven’t been tested yet,&amp;#160; the monkeys aren’t yet on display (or at least we haven’t written the code for them yet) so all we have to interact with for now is an IMonkey interface as follows:&lt;/p&gt;  &lt;pre class="brush:csharp"&gt;public interface IMonkey&lt;br /&gt;{&lt;br /&gt; string Name { get; set; }&lt;br /&gt; bool TryAddFleas(int numberOfFleas);&lt;br /&gt; int CurrentFleaCount();&lt;br /&gt; void Clean();&lt;br /&gt; bool IsAwake(DateTime timeOfDay);&lt;br /&gt; void BananaReady(object sender, BananaEventArgs e);&lt;br /&gt; event EventHandler&amp;lt;EventArgs&amp;gt; Dance;&lt;br /&gt; IZooKeeper Keeper();&lt;br /&gt;}&lt;/pre&gt;&lt;h4&gt;The IZooKeeper Interface and the ZooKeeper&lt;/h4&gt;&lt;p&gt;Monkeys have Keepers, and since all staff in any organisation are just “resources” and thus completely interchangeable (grrr – don’t get me started on this kind of thinking!) we should have an IZooKeeper interface.&amp;#160; We are also lucky enough to actually have a standard ZooKeeper definition as well.&amp;#160; Here’s the code:&lt;/p&gt;&lt;pre class="brush:csharp"&gt;public interface IZooKeeper&lt;br /&gt;{&lt;br /&gt; event EventHandler&amp;lt;BananaEventArgs&amp;gt; OnBananaReady;&lt;br /&gt; IMonkey AssignedMonkey { get; set; }&lt;br /&gt; void CleanMonkey();&lt;br /&gt; void FeedMonkeys();&lt;br /&gt;}&lt;/pre&gt;&lt;pre class="brush:csharp"&gt;public class ZooKeeper : IZooKeeper&lt;br /&gt;{&lt;br /&gt; public event EventHandler&amp;lt;BananaEventArgs&amp;gt; OnBananaReady;&lt;br /&gt;&lt;br /&gt; public IMonkey AssignedMonkey { get; set; }&lt;br /&gt; &lt;br /&gt; public void CleanMonkey()&lt;br /&gt; {&lt;br /&gt;  if (AssignedMonkey == null) return;&lt;br /&gt;  if (AssignedMonkey.CurrentFleaCount() &amp;lt; 10) return;&lt;br /&gt;  if (AssignedMonkey.IsAwake(DateTime.Now))&lt;br /&gt;   AssignedMonkey.Clean();&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public void FeedMonkeys()&lt;br /&gt; {&lt;br /&gt;  if (OnBananaReady != null)&lt;br /&gt;  {&lt;br /&gt;   OnBananaReady(this, new BananaEventArgs(true));&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public class BananaEventArgs : EventArgs&lt;br /&gt;{&lt;br /&gt; public BananaEventArgs(bool isRipe)&lt;br 
