Nothin but .NET course - Tips and Tricks - Day 1

I'm currently popping in and out of Jean-Paul Boodhoo's Nothin but .NET course for the week and having a blast. We brought JP in-house to the company I'm at right now for his .NET training course, which is arguably probably the best .NET course out there. Period.

I don't claim to be an expert (at least I hope I've never claimed that) and I'm always looking for ways to improve myself and add new things to my gray matter that I can hopefully recall and use later. Call them best practices, call them tips and tricks, call them rules to live by. Over the course of the week I'll be blogging each day about what's going on so you can live vicariously through me (but you really need to go on the course to soak in all of JP and his .NET goodness).

So here with go with the first round of tips:

File -> New -> Visual Studio Solution

This is a cool thing that I wasn't aware of (and makes me one step closer to not using the mouse, which is a good thing). First, we have a basic empty Visual Studio .sln file. This is our template for new solutions. Then create new registry file (VisualStudioHack.reg or whatever you want to call it) with these contents:

Windows Registry Editor Version 5.00

"FileName"="Visual Studio Solution.sln"

Double-click the .reg file and merge the changes into your registry. The result is that you now will have a new option in your File -> New menu called "Microsoft Visual Studio Solution". This is based on your template (the empty .sln file) that you provide so you can put whatever you want in here, but it's best to just snag the empty Visual Studio Solution template that comes with Visual Studio. Very handy when you just need a new solution to start with and don't want to start up Visual Studio and navigate through all the menus to do this.


MbUnit rocks over NUnit. My first exposure to the row test and while you can abuse the feature, it really helps cut down writing a ton of tests or (worse) one test with a lot of entries.

In NUnit let's say I have a test like this:

   35 [Test]

   36 public void ShouldBeAbleToAddTwoPositiveNumbers()

   37 {

   38     int firstNumber = 2;

   39     int secondNumber = 2;

   40     Calculator calculator = new Calculator();

   41     Assert.AreEqual(firstNumber + secondNumber, calculator.Add(firstNumber, secondNumber));

   42 }

And I need to test boundary conditions (a typical thing). So I've got two options. First option is to write one test per condition. The second is to write a single test with multiple asserts. Neither is really appealing. Having lots of little tests is nice, but a bit of a bear to maintain. Having a single test with lots of asserts means I have to re-organize my test (hard coding values) and do something like this in order to tell which test failed:

   33 [Test]

   34 public void ShouldBeAbleToAddTwoPositiveNumbers()

   35 {

   36     Calculator calculator = new Calculator();

   37     Assert.AreEqual(2 + 2, calculator.Add(2, 2), "could not add positive numbers");

   38     Assert.AreEqual(10 + -10, calculator.Add(10, -10), "could not add negative number");

   39     Assert.AreEqual(123432 + 374234, calculator.Add(123432, 374234), "could not add large numbers");

   40 }

Or something like, but you get the idea. Tests become ugly looking and they feel oogly to maintain. Enter MbUnit and the RowTest. The test above becomes parameterized that looks like this:

   28 [RowTest]

   29 public void ShouldBeAbleToAddTwoPositiveNumbers(int firstNumber, int secondNumber)

   30 {

   31     Assert.AreEqual(firstNumber + secondNumber, calculator.Add(firstNumber, secondNumber));              

   32 }

Now I can simply add more Row attributes, passing in the various boundary conditions I want to check like so:

   23 [Row(2,2)]

   24 [Row(10, -10)]

   25 [Row(123432, 374234)]

   26 [RowTest]

   27 public void ShouldBeAbleToAddTwoPositiveNumbers(int firstNumber, int secondNumber)

   28 {

   29     Assert.AreEqual(firstNumber + secondNumber, calculator.Add(firstNumber, secondNumber));              

   30 }

That's cool (and almost enough to convince me to switch) but what's uber cool about this? In the MbUnit GUI runner, it actually looks like separate tests and if a Row fails on me, I know exactly what it was. It's a failing test out of a set rather than a single test with one line out of many asserts.


As with any tool, you can abuse this so don't go overboard. I think for boundary conditions and anywhere your tests begin to look crazy, this is an awesome option. I haven't even scratched the surface with MbUnit and it's database integration (I know, databases in unit tests?) but at some point you have to do integration testing. What better way to do it than with unit tests. More on that in another blog.

Subject Under Test

This term was coined (at least my first exposure to it) was from Gerard Meszaros excellent book xUnit Testing Patterns and he uses it throughout. It makes sense as any unit test is going to be testing a subject so therefore we call it the Subject Under Test. One nomenclature that JP is using in his tests is this:

   15 private ICalculator calculator;


   17 [SetUp]

   18 public void SetUpCalculatorTest()

   19 {

   20     calculator = CreateSUT();

   21 }


   23 private ICalculator CreateSUT()

   24 {

   25     return new Calculator();

   26 }

So basically every unit test has a method called CreateSUT() (where appropriate) which creates an object of whatever type you need and returns it. I'm not sure this replaces the ObjectMother pattern that I've been using (and it's really not a pattern but more of a naming convention) but again, it's simple and easy to read. A nice little tidbit you pick up.

In doing this, my mad ReSharper skills got the best of me. Normally I would start with the CreateSUT method, which in this case returns an ICalculator by instantiating a new Calculator class. Of course there's no classes or interfaces by this name so there's two options here. One is to write your test and worry about the creation later, the other is to quickly create the implemenation. At some point you're going to have to create it anyways in order to compile, but I like to leave that until the last step.

Under ReSharper 2.x you could write your test line by writing CreateSUT() then press CTRL+ALT+V (introduce variable). However since ReSharper 2.5 (and it's still there in 3.x) you can't do this. ReSharper can't create the ICalculator instance (in memory) in order to walk through the method table (which would give you intellisense). So the simple thing is to write the CreateSUT() method and just whack ALT+ENTER a few times to create the class and interface.

Thread safe initialization using a delegate

I have to say that I never looked into how to initialize event handlers in a thread-safe way. I've never had to do it in the past (I don't work with events and delegates a lot) but this is a great tip. If you need to initialize an event handler but do it in a thread-safe way here you go:

private EventHandler<CustomTimerElapsedEventArgs> subscribers = delegate { };

It's the simple things in life that give me a warm and fuzzy each day.

Event Aggregators and Declarative Events

We spent the better part of the day looking at events, delegates, aggregators and whatnot. This is apparently new to the course that he's just added and hey, that's what Agile is about. Adapting to change.

Anyways, as we dug into it I realized how little I knew about events and how dependent I was on the framework to help me with those object.EventName += ... snippets I would always write. Oh how wrong that was as we got into loosely coupled event handlers (all done without dynamic proxies, which comes later). It's pretty slick as you can completely decouple your handlers and this is a good thing. For example, here's a simple test that just creates an aggregator, registers a subscriber, and checks to see if the event fired.

First the test:

   25 [Test]

   26 public void ShouldBeAbleToRegisterASubscriberToANamedEvent()

   27 {

   28     IEventAggregator aggregator = CreateSUT();

   29     aggregator.RegisterSubscriber<EventArgs>("SomethingHappened", SomeHandler);

   30     eventHandlerList["SomethingHappened"].DynamicInvoke(this, EventArgs.Empty);

   31     Assert.IsTrue(eventWasFired);

   32 }

Here's the delegate that will handle firing the event:

   58 private void SomeHandler(object sender, EventArgs e)

   59 {

   60     eventWasFired = true;

   61 }

And here's part of the aggregator class which simply manages subscribers in an EventHandlerList:

    7 public class EventAggregator : IEventAggregator

    8 {

    9     private EventHandlerList allSubscribers;


   11     public EventAggregator(EventHandlerList allSubscribers)

   12     {

   13         this.allSubscribers = allSubscribers;

   14     }


   16     public void RegisterSubscriber<ArgType>(string eventName, EventHandler<ArgType> handler)

   17         where ArgType : EventArgs

   18     {

   19         allSubscribers.AddHandler(eventName, handler);

   20     }


   22     public void RaiseEvent<ArgType>(string eventName, object sender, ArgType empty) where ArgType : EventArgs

   23     {

   24         EventHandler<ArgType> handler = (EventHandler<ArgType>) allSubscribers[eventName];

   25         handler(sender, empty);

   26     }


   28     public void RegisterEventOnObject<ArgType>(string globalEventName, object source, string objectEventName)

   29         where ArgType : EventArgs

   30     {

   31     }

   32 }

Again, basic stuff but lets you decouple your events and handlers so they don't have intimate knowledge of each other and this is a good thing.

A very good thing.

BTW, as we extended this we sort of built CAB-like functionality on the first day and in a few hours (only event publishers and subscribers though). We just added two attribute classes to handle publishers and subscribers (rather than subscribing explicitly). It wasn't anywhere near as complete as Jeremy Miller's excellent Build your own CAB series, but none-the-less it was a simple approach to a common problem and for my puny brain, I like simple.

It was fun and appropriate as the team is using CAB so the guys in the room are probably more positioned to understand CAB's EventBroker now.

Other Goodies and Tidbits

JP uses a nifty macro (that I think I'll abscond and try out myself) where he types the test name in plain English but with spaces like so:

Should be able to respond to events from framework timer

Then he highlights the text and runs the macro which replaces all spaces with underscores:


I'm indifferent about underscores in method names and it's a no-no in production code, but for test code I think I can live with it. I reads well and looks good on the screen. Using a little scripting, reflection, or maybe NDepend I could spit out a nice HTML page (reversing the underscores back to spaces) and form complete sentences on my test names. This can be a good device to use with business users or other developers as you're trying to specify intent for the tests. I have to admit it beats the heck out of this:


Doesn't it?

Wrap up

The first day has been a whirlwind for the team as they're just soaking everything in and making sense of it. For me, I'm focusing on using my keyboard more and getting more ReSharper shortcuts embedded into my skull. I've now got MbUnit to go plow through, check out the other fixtures and see what makes it tick and take the plunge from NUnit if it makes sense (which I think it does as I can still use my beloved TestDriven.NET with MbUnit as well!).

More Nothin

JP is doing this 5-day intensive course that stretches people to think outside the box of how they typically program and delve into realms they have potentially not event thought about yet, about one per month. For the next little while (he's a busy dude) he's going to be travelin' dude. In October he'll be in New York City. In September he'll be in London, England and other locations are booked up into the new year.

If you want to find out more details about the course itself, check out the details here and be sure to get on board with the master.


Comments have been disabled for this content.