snappyco.de

rss

Shouldly

Shouldly is the "should" style test library you reach for when you've said "WTF just happened?!".

Pop quiz friend! Fix this error:

Expected 1337 but was 0

Ok, let me just set a breakpoint, fire up my debugger and...

Now stop right there! Put down that debugger and pick up Shouldly! It transforms your error into this:

contestant.Points should be 1337 but was 0

Ahhh! I get it, I need more points! But how do I write these magical tests? I LOVE MY NUNIT ASSERT.THAT!!

Really.

Yes

Really??

Yes!!

You like writing this?

Assert.That(contestant.Points, Is.EqualTo(1337));

YES!!

What if you could write your test like this!

contestant.Points.ShouldBe(1337);

ZOMG!1 Wow, what else can it do??

Well apart from having a whole host of "Shoulds":

ShouldBe
ShouldBeGreaterThan
ShouldBeLessThan
ShouldContain
ShouldNotContain
ShouldBeCloseTo

Wait, wait, shouldBeCloseTo?? Come on buddy, we're testing here, it's a precise activity, it's either equal or not, pass or fail, red or green capeche!?

Uh huh. Let's just say for interest, that you're testing XML in a string.

Ewwwwwww

It looks like this:

<enterprisey-configuration-block><enterprisey-logging>
    <loglevel alert="brown" />
  </enterprisey-logging>    </enterprisey-configuration-block>

Who formatted that so ugly!? Now we have to write an ugly string test to match it right? Wrong! I don't actually care if it doesn't match the whitespace properly:

xmlConfig.ShouldBeCloseTo(@"
<enterprisey-configuration-block>
  <enterprisey-logging>
    <loglevel alert='brown' />
  </enterprisey-logging>    
</enterprisey-configuration-block>");

Now we have a nice flexible test and our enterprise can sleep safe knowing it's precious xml is being cared for.

Notice the quotes around "brown"? Shouldly will do the compare, ignoring whitespace and not fussing over which quotes you used.

Ok Dave, this is cool and you're really awesome and stuff but what about when I get null refs in my mock tests or expectation violations?

Rhino.Mocks.Exceptions.ExpectationViolationException:
IContestant.PlayGame("Shouldly"); Expected #1, Actual #0

I still have to run up my debugger to figure out why my method didn't get called right??

You could do that.

Or, you could write your test like this:

contestant.ShouldHaveBeenCalled(c => c.PlayGame("Shouldly"));

and get this!

Expected:
  IContestant.PlayGame("Shouldly");
Recorded:
  IContestant.PlayGame("Debugging");
  IContestant.PlayGame("Logging");
  IContestant.PlayGame("Drinking coffee");
  IContestant.PlayGame("Commenting out test");

Now we see what's really happening here!

Wowee! How do I get started?

Just download or git clone http://github.com/snappycode/shouldly and either include it the source in your project or run msbuild and copy the dll into your repository.

Oh and feel free to fork and improve friends, let's all help each other out.

kick it on DotNetKicks.com

Fixing C# - Part II

This is part 2 of my series on making c# a nice language to work with. Previously I gave you flexible string quotation, today I present:

Regular Expressions as native types

Ever written code like this?

var money = Regex.Replace(@" $ 1,337", @"[\$\s\,]", "");

Regular expressions are so important to programming that they should be catered for in the language. At least Ruby and Javascript agree with me.

If c# understood regular expressions as a native type, we could have code like this:

var money = @" $ 1,337".Replace(/[\$\s\,]/, "")

You can instantly spot the regex, it doesn't have to be @ encoded into a string. We can also then have a replace method overload that takes a string or a regex type without any new Regex() bs.

Assert Awesomely

I've been working on nicer test code and error messages. Here's what I started with:

var steve = new Zombie();
Assert.That(steve.Mood, Is.EqualTo("I'm hungry for brains!"));

Which tells me

Expected string length 22 but was 27. Strings differ at index 1.
Expected: "I'm hungry for brains!"
But was:  "I want to shuffle aimlessly"
------------^

That's crap!

What I really wanted was a short behaviour style test and an error message that told me steve's mood was not what I expected it to be.

First the test syntax can be fixed with an extension method:

public static void ShouldBe<T>(this T actual, T expected)
{
    actual.AssertAwesomely(Is.EqualTo(expected), expected);
}

The test:

var steve = new Zombie();
steve.Mood.ShouldBe("I'm hungry for brains!");

Nice!

Now on to the error message. I did some experimenting with c#'s reflection and stack trace api and turned up the file and line number of the test that went kaboom! I then applied some loose morals and sweet regular expressions to turn the error into this:

steve.Mood 
    should be 
        "I'm hungry for brains!" 
    but was 
        "I want to shuffle aimlessly"

Not bad if I do say so myself.

And here's the code (Feel free to fork and improve). http://gist.github.com/252084

Fixing C#

For better or worse I spend a lot of time coding in c#. Bouncing back and forth between c#, ruby, javascript and objective-c I inevitably reflect a lot on the different language features and let me tell you that c# is my least favourite language to work in. But with a few improvements it could be made a passable programming language!

Of course I understand that c# is a static language and won't ever have all the awesomeness of a real dynamic type system, so I'm not advocating a full overhaul, just a few additions.

I'm going to start simple in this article and cover more in the future. So for today I present:

Flexible string quotation

Ever written code like this?

var message = "I heart \"unnecessary\" quotations";

All that messy quote escaping could be gone if we can quote strings with double or single quotes. We could rewrite that like this:

var message = 'I heart "unnecessary" quotations';

Much nicer!

I'm a sydney based software developer. I currently build stuff with ruby, c# and objective-c.

dave@snappyco.de

I'm on Twitter

  • Thanks japan, now I REALLY hate Sydney trains. 9 minute wait?!
  • Drugs are bad mmmkay http://bit.ly/9Jl7v1
  • NSCoders: [[NSArray arrayWithObjects:...] retain] or [[NSArray alloc] initWithObjects:...]
  • Anyone wanna give me a desk to work at today in surry hills?
  • Back in oz after a wicked japan #rubykaigi trip. Domo arigato japan!

I'm on Github