RSS
Facebook
Twitter

Monday, November 05, 2012

Making string based method strongly typed

I can't believe they implemented this way – was the first thought on my mind…
The method in question is part of the new(er) NUnit’s constraint based model specifically the one used to assert a property value.
Before we dive deeper a few words on the constraint based model:

constraint based model

Most unit testing framework have similar way of writing assertions – if you’ve wrote a test in the past it probably looked something like this:
Assert.AreEqual(4, 2 + 2);

NUnit has another API for assertion which is more to my liking and using it for the above assertion would look like this:

Assert.That(2 + 2, Is.EqualTo(4));

Using this API I can write pretty cool things:

// Simple values
Assert.That(someInt, Is.Negative);
Assert.That(someString, Is.Not.Null.And.Not.Empty);
Assert.That(someObject, Is.Null);

// Collections
Assert.That(someList, Is.Not.Empty);
Assert.That(intArray, Is.All.GreaterThan(10); // All intergers in array are bigger than 10
Assert.That(array, Has.Exactly(1).GreaterThan(3)); // Exactly one item bigger than 3
Assert.That(someEnumerable, Is.All.Property("MyProp").EqualTo(25));

I have an issue with the last line. I don’t like writing string based code – mainly it tend to break on trivial refactoring and writing assertion that could break on runtime  just because someone has changed the property name seems just wrong.
I have as compiler and I’m not afraid to use it – the least you can do is give me compile time errors!

The solution

I’ve wanted to add another method to Is.All and so I’ve used one of fluent API writers friend – the extension method and I got the following API:

Assert.That(someEnumerable, Is.All.Property<MyType>(m => m.MyProp).EqualTo(25));

Using black lambda tricks I was able to create a strongly typed API that receive a property.

After I had my entry point sorted out all I needed to do is write (and copy shamelessly) some cool Expression based code to get the property name and it works!

Here is the full code:

public static ResolvableConstraintExpression Property<T>(
  this NUnit.Framework.Constraints.ConstraintExpression expression,   Expression<func<T, object>> lambda)
{
    MemberExpression memberExpression = null;
    switch (lambda.Body.NodeType)
    {
        case ExpressionType.Convert:
            // lambda is obj => Convert(obj.Prop) - the operand of conversion is our member expression
            var unary = lambda.Body as UnaryExpression;
            if (unary == null)
            {
                Assert.Fail("Cannot parse expression");
            }
            memberExpression = unary.Operand as MemberExpression;
            break;

        case ExpressionType.MemberAccess:
            // lambda is (obj => obj.Prop) - the body is the member expression
            memberExpression = lambda.Body as MemberExpression;
            break;

        default:
            Assert.Fail("Cannot parse expression");
            break;
    }

    if (memberExpression == null ||  string.IsNullOrEmpty(memberExpression.Member.Name))
    {
        Assert.Fail("Labda body is not MemberExpression - use only properties");
    }

    var propertyName = memberExpression.Member.Name;

    return expression.Property(propertyName);
}


I hope it would help at least one reader who cannot accept a string based API.

Happy Coding…

Friday, November 02, 2012

Unit level BDD using SubSpec

In the BDD (Behavior Drive Development) there are two groups of frameworks. The first group contains tools such as SpecFlow and nBehave to name a few all of which uses (at least) two files – one contains the scenarios/behaviors in plain English (more or less) and the other the code that makes it all happen.
I want to discuss the other type – the one that contains “unit level” BDD. Unlike the first group the behaviors are written inside the code where only us developers can read them – you can always try to convince your product/marketing manager to read your code but it tends to go downhill from there…
The reason I’m telling you this is that this week I discovered a very interesting tool – SubSpec. It uses extension methods on System.String, lambdas and some nifty XUnit tricks to make BDD accessible and easy to use.

Getting started

Using SubSpec is simple. After creating a new project add a reference to SubSpec using NuGet:
image
Adding SubSpec via NuGet will also make sure that you have the required dependencies - XUnit and XUnit.Extensions:
image
And you’re ready to go…

Writing the first behavior

Like any other test SubSpec test is divided into three parts – Context, Action and Verifications.
The entry point to all three is a string.
Writing a new test is done by following these four steps:
  1. Add Specification attribute to the test method
  2. Use Context to initialize the class under test
  3. Use Do to perform an action
  4. Use Assert to test for success/failure
[Specification]
public void StoreWithOneProduct()
{
    const string productId = "prod-1";

    var store = default(Store);

    "Given a new store".Context(() => store = new Store());

    "with one product".Do(() => store.AddProduct(productId));

    "The store has that product".Assert(() => Assert.True(store.HasProduct(productId)));
}

Because SubSpec is built on top of XUnit any runner that is able to run XUnit tests can run these specifications. I’ve used the freely available XUnit Runner for VS2012 and got this results:

image

As you can see the strings used are shown as the test name making it easy to understand what is being tested.

Wait there’s more

One problem I always have is tests that test more than one assertion. The problem is that if one assertion fail we cannot know what is the status with the other assertions being tested. SubSpec has a cool solution – run each assertion as a different test:

[Specification]
public void StoreWithOneProduct()
{
    const string productId = "prod-1";

    var store = default(Store);

    "Given a new store".Context(() => store = new Store());

    "with one product".Do(() => store.AddProduct(productId));

    // Usually this line would cause the line after it to fail
    "Customer can purchase that product".Assert(
        () => Assert.True(store.BuyProduct(productId)));

    "The store has that product".Assert(() => Assert.True(store.HasProduct(productId)));
}

image

SubSpec has actually run two separate tests one for each assertion. But in case you want a proof – let’s write a test with a failing assertion:

[Specification]
public void StoreWithOneProduct()
{
    const string productId = "prod-1";

    var store = default(Store);

    "Given a new store".Context(() => store = new Store());

    "with one product and product is purchased".Do(() => 
        {
            store.AddProduct(productId);
            store.BuyProduct(productId);
        });

    // this is wrong - stop copy/pasting your tests :)
    "Customer can purchase that product".Assert(
        () => Assert.True(store.BuyProduct(productId)));

    "The store does not have that product".Assert(() => Assert.False(store.HasProduct(productId)));
}

And the result of this test run is:
image

The 2nd assertion passed since it was run independently from the 1st failing assertion.

Running all of the assertions in one test

What if you do need to run all of the assertions in one test? Simple, use Observe keyword that cause all of the assertions created within it to run in the same context.

[Specification]
public void PushSpecification()
{
    var stack = default(Stack);
    "Given a new stack".Context(() => stack = new Stack());

    string element = "first element";
    "with an element pushed onto it".Do(() => stack.Push(element));

    "the stack is not empty".Observe(() => Assert.False(stack.IsEmpty));
    "the stacks Top is the pushed element".Observe(() => Assert.Equals(element, stack.Top));
}


SubSpec Assert


And yes – you can mix Assert and Observe in the same test – and Godspeed on that.

Is that All

Yes!

This is all you need to know in order to use SubSpec, there’s nothing more. It’s simple compact and works. Johannes Rudolph - the creator of SubSpec has taken pains not to add anything else to his frameworks keeping it simple and very small.
But so far I failed to find any limitation that prevents me from using SubSpec and XUnit to quickly add behaviors to my tests.

Happy coding…
Related Posts Plugin for WordPress, Blogger...