Feeds:
Posts
Comments

Archive for the ‘DSL’ Category

This is an experiment along the lines of the Anti-If Campaign using the FizzBuzz Kata as a test base. The FizzBuzz problem statement is as follows:

Write a program that prints the numbers in order from 1 to 100, with the exception that when a number is a multiple of 3, it prints “Fizz”. When a number is a multiple of 5, it prints “Buzz”. And when a number is multiple of both, it prints “FizzBuzz”. In all other cases, it just prints the decimal representation of the number.

First the test for the Fizz portion.

public class When_asked_to_GetFizzBuzz
{
	private List<int> _inputs;
	private List<Pair<int, string>> _result;

	[SetUp]
	public void BeforeEachTest()
	{
		_inputs = new List<int>();
	}

	[Test]
	public void Given_numbers_divisible_by_3_that_are_indivisible_by_5_should_get__Fizz()
	{
		Test.Verify(
			with_numbers,
			that_are_divisible_by_3,
			that_are_not_divisible_by_5,
			when_asked_to_get_FizzBuzz,
			should_get_same_number_of_outputs_as_inputs,
			should_get__Fizz__for_each
			);
	}

	private void with_numbers()
	{
		_inputs.AddRange(Enumerable.Range(1, 100));
	}

	private void that_are_divisible_by_3()
	{
		_inputs.RemoveAll(x => x % 3 != 0);
	}

	private void that_are_not_divisible_by_5()
	{
		_inputs.RemoveAll(x => x % 5 == 0);
	}

	private void when_asked_to_get_FizzBuzz()
	{
		_result = _inputs.GetFizzBuzz().ToList();
	}

	private void should_get_same_number_of_outputs_as_inputs()
	{
		_result.Count.ShouldBeEqualTo(_inputs.Count);
	}

	private void should_get__Fizz__for_each()
	{
		_result.All(x => x.Second == "Fizz").ShouldBeTrue();
	}

and its implementation

public static class IntExtensions
{
	public static IEnumerable<Pair<int, string>> GetFizzBuzz(this IEnumerable<int> values)
	{
		return values
			.Select(x => new Pair<int, string>(x, "")
			             	.When(NumberIsDivisibleBy3,
			             	      SetResultToFizz));
	}

	private static Func<Pair<int, string>, bool> NumberIsDivisibleBy3
	{
		get { return y => y.First % 3 == 0; }
	}

	private static Action<Pair<int, string>> SetResultToFizz
	{
		get { return y => y.Second = "Fizz"; }
	}
}

Now the DSL

public static class TExtensions
{
	public static T When<T>(this T item, Func<T, bool> condition, Action<T> doIfTrue)
	{
		if (condition(item))
		{
			doIfTrue(item);
		}
		return item;
	}
}

Pretty interesting. Now the Buzz test:

	[Test]
	public void Given_numbers_indivisible_by_3_that_are_divisible_by_5_should_get__Buzz()
	{
		Test.Verify(
			with_numbers,
			that_are_not_divisible_by_3,
			that_are_divisible_by_5,
			when_asked_to_get_FizzBuzz,
			should_get_same_number_of_outputs_as_inputs,
			should_get__Buzz__for_each
			);
	}

	private void that_are_not_divisible_by_3()
	{
		_inputs.RemoveAll(x => x % 3 == 0);
	}

	private void that_are_divisible_by_5()
	{
		_inputs.RemoveAll(x => x % 5 != 0);
	}

	private void should_get__Buzz__for_each()
	{
		_result.All(x => x.Second == "Buzz").ShouldBeTrue();
	}

and implementation change

public static class IntExtensions
{
	public static IEnumerable<Pair<int, string>> GetFizzBuzz(this IEnumerable<int> values)
	{
		return values
			.Select(x => new Pair<int, string>(x, "")
			             	.When(NumberIsDivisibleBy3,
			             	      SetResultToFizz)
			             	.When(NumberIsDivisibleBy5,
			             	      SetResultToBuzz));
	}

	private static Func<Pair<int, string>, bool> NumberIsDivisibleBy5
	{
		get { return y => y.First % 5 == 0; }
	}

	private static Action<Pair<int, string>> SetResultToBuzz
	{
		get { return y => y.Second += "Buzz"; }
	}

Next a test for the FizzBuzz part:

	[Test]
	public void Given_numbers_divisible_by_3_and_5_should_get__FizzBuzz()
	{
		Test.Verify(
			with_numbers,
			that_are_divisible_by_3,
			that_are_divisible_by_5,
			when_asked_to_get_FizzBuzz,
			should_get_same_number_of_outputs_as_inputs,
			should_get__FizzBuzz__for_each
			);
	}

	private void should_get__FizzBuzz__for_each()
	{
		_result.All(x => x.Second == "FizzBuzz").ShouldBeTrue();
	}

and implementation change

public static class IntExtensions
{
	public static IEnumerable<Pair<int, string>> GetFizzBuzz(this IEnumerable<int> values)
	{
		return values
			.Select(x => new Pair<int, string>(x, "")
			             	.When(NumberIsDivisibleBy3,
			             	      SetResultToFizz)
			             	.When(NumberIsDivisibleBy5,
			             	      AppendBuzzToResult));
	}

	private static Action<Pair<int, string>> AppendBuzzToResult
	{
		get { return y => y.Second += "Buzz"; }
	}

Finally, a test for getting the number when it doesn’t match any of the filters:

	[Test]
	public void Given_numbers_indivisible_by_3_and_5_should_get_the_number()
	{
		Test.Verify(
			with_numbers,
			that_are_not_divisible_by_3,
			that_are_not_divisible_by_5,
			when_asked_to_get_FizzBuzz,
			should_get_same_number_of_outputs_as_inputs,
			should_get_the_number_for_each
			);
	}

	private void should_get_the_number_for_each()
	{
		_result.All(x => x.Second == x.First.ToString()).ShouldBeTrue();
	}

implementation change

public static class IntExtensions
{
	public static IEnumerable<Pair<int, string>> GetFizzBuzz(this IEnumerable<int> values)
	{
		return values
			.Select(x => new Pair<int, string>(x, "")
			             	.When(NumberIsDivisibleBy3,
			             	      SetResultToFizz)
			             	.When(NumberIsDivisibleBy5,
			             	      AppendBuzzToResult)
			             	.Unless(NumberIsDivisibleBy3Or5,
			             	        SetResultToTheNumber));
	}

	private static Func<Pair<int, string>, bool> NumberIsDivisibleBy3Or5
	{
		get { return y => NumberIsDivisibleBy3(y) || NumberIsDivisibleBy5(y); }
	}

	private static Action<Pair<int, string>> SetResultToTheNumber
	{
		get { return y => y.Second = y.First.ToString(); }
	}

and DSL addition

public static class TExtensions
{
	public static T Unless<T>(this T item, Func<T, bool> condition, Action<T> doIfNotTrue)
	{
		if (!condition(item))
		{
			doIfNotTrue(item);
		}
		return item;
	}

This is an interesting start but rather anemic at the moment. Desired:
* optionally provide multiple actions for a particular match
* optionally handle exceptions for each one
* optionally break out of the DSL chain, or
* skip to a previous/future named match instead of falling through

e.g.

   x.When(IsDivisibleBy3)
        .Do(DoubleTheNumber)
            .On<ArgumentOutOfRangeException>()
               .LogIt(...)
               .SkipToEnd()
        .Do(Add3ToTheNumber)
     .When(IsDivisibleBy5)
        ...

The code for this post is available on github.

Advertisements

Read Full Post »

I’ve been using FluentAssert for my test-driven development because it abstracts away the test details leaving only a highly readable test. I also find that it enforces a certain level of separation of concerns and moistness in the tests without spreading portions of the tests through an inheritance stack. For example:

[TestFixture]
public class When_asked_if_a_string_is_null_or_empty_with_optional_trim
{
	[Test]
	public void Given_a_null_string_and_trim_is_false()
	{
		Test.Static()
			.When(asked_if_a_string_is_null_or_empty)
			.With(a_null_string)
			.With(trim_set_to_false)
			.Should(return_true)
			.Verify();
	}
}

with test details

	private void asked_if_a_string_is_null_or_empty()
	{
		_result = _input.IsNullOrEmpty(_trim);
	}

	private void a_null_string()
	{
		_input = null;
	}

	private void trim_set_to_false()
	{
		_trim = false;
	}

	private void return_true()
	{
		_result.ShouldBeTrue();
	}

Today I was reading about features of the Factor language, specifically the part about naming code not values and that it is stack based instead of function based, and it occurred to me that a DSL that has no parameters is a lot like a contactenative language except for the extra visual bits like . and () joining the method calls together. This started me to thinking how I might be able to further reduce the ceremonial aspects of the test framework.

One nice thing about a DSL is it can be coded to be context sensitive, meaning it might not let you put an assertion call (e.g. Should()) before an arranging call (e.g. With()). A lower ceremony framework wouldn’t necessarily offer that level of protection but in return we might get tests that are even easier to read and that don’t have an execution command appended. That’s one thing that bothers me about the current framework, the command of execution, .Verify(), is required at the end of each test chain in order to fire off the framework test runner. If you leave it off the NUnit test runs but the BDD test doesn’t and you get a false green. I wondered if I could address that annoyance as well as make something more concatenative. Here’s the result:

[TestFixture]
public class When_asked_if_a_string_is_null_or_empty_with_optional_trim
{
	[Test]
	public void Given_a_null_string_and_trim_is_false()
	{
		Test.Verify(
			with_a_null_string,
			with_trim_set_to_false,
			when_asked_if_a_string_is_null_or_empty,
			should_return_true
			);
	}
}

Instead of chaining methods with Action parameters we’re passing all the Actions as a params array. The DSL methods .When(part_A) .Should(part_B) etc. are simply replaced with their parameters part_A, part_B but we can change the Action names to include the When, Should etc. in order to retain readability in the test and its output. The call to Verify becomes the only framework method we call and we can give it the test Actions to run in the order we want to run them instead of having to give the Action for the method under test (When()) first – this is about as close as I think I can get to a Forth style Word command line in C#.

I’ve integrated this variation with the existing test result reporting so if you start your Action names with known keywords you’ll continue to get uppercased keyword output, e.g.:

WITH a null string
WITH trim set to false
WHEN asked if a string is null or empty
SHOULD return true - FAILED

  Expected: True
  But was:  False

I’ve added this as an alternative DSL path in the FluentAssert framework on GitHub if you want to try it out.

Enjoy!

Read Full Post »

In the previous part of this series we built a simple Inversion of Control container called LeafIoC that could only return objects that had default constructors and were specifically configured. In this post we’ll figure out how to support singleton objects in the container.

In a nutshell, here’s a test describing the capability we want (using FluentAssert):

	[Test]
	public void Given_a_type_that_has_been_configured_as_a_singleton()
	{
		Test.Static()
			.When(asked_to_get_an_instance)
			.With(a_type_that_has_been_configured_as_a_singleton)
			.Should(get_a_non_null_instance)
			.Should(get_the_same_instance_every_time)
			.Verify();
	}

Now lets think about the as_a_singleton part for a minute. Do we want a new method on the container like the following?

	public static TInterface GetSingletonInstance<TInterface>()

We could certainly do that but should the user really have to think about whether the object is a singleton or not when asking for one? No, this seems like the wrong end of the system to be specifying a characteristic like Singleton. How about:

	public static void ConfigureSingleton<TInterface, TImplementation>()

This would definitely work but as we add more functionality the public interface of LeafIoC will get really cluttered. What if we also implemented named instances and now we want named static instances? Would we make the name longer?

	public static void ConfigureNamedSingleton<TInterface, TImplementation>(string name)

with variant?

	public static void ConfigureNamedInstance<TInterface, TImplementation>(string name)

No. We need to plan for the ability to compose characteristics using a domain specific language (DSL) with usage like:

	LeafIoC.Configure<ICalculator,ScientificCalculator>()
	          .Named("Scientific")
	          .AsSingleton();

To do that we’re going to have to store some kind of resolver (an object that can evaluate the DSL state at the last possible moment) in the internal dictionary. We already have tests around all the existing capability so lets take this opportunity to refactor the code to better be able to support singletons.

First we’ll create a placeholder class for the resolver:

public class ResolutionInfo
{
}

Then add a property for the object we were previously storing in the dictionary:

public class ResolutionInfo
{
	public MulticastDelegate Initializer { get; set; }
}

Change the dictionary to store it:

	private static readonly Dictionary<Type, ResolutionInfo> Configuration 
		= new Dictionary<Type, ResolutionInfo>();

And fix resultant breakage in LeafIoC:

	public static void Configure<TInterface, TImplementation>() where TImplementation : TInterface, new()
	{
		var type = typeof(TInterface);
		Configuration[type] = new ResolutionInfo
			{
				Initializer = (Func<TInterface>)(() => new TImplementation())
			};
	}

	public static TInterface GetInstance<TInterface>()
	{
		var type = typeof(TInterface);
		ResolutionInfo value;
		if (!Configuration.TryGetValue(type, out value))
		{
			throw new ConfigurationErrorsException(string.Format("No instance of {0} has been configured.", type.FullName));
		}
		var instance = ((Func<TInterface>)(value.Initializer)).Invoke();
		return instance;
	}

Finally, run the tests to make sure we’re still in a good state.

Next, to implement the DSL we’ll have to return something from Configure that we can build upon. First the thing to return:

public class ResolutionContext
{
}

Then the change to Configure():

	public static ResolutionContext Configure<TInterface, TImplementation>() where TImplementation : TInterface, new()
	{
		var type = typeof(TInterface);
		var resolutionInfo = new ResolutionInfo
			{
				Initializer = (Func<TInterface>)(() => new TImplementation())
			};
		Configuration[type] = resolutionInfo;

		return new ResolutionContext(resolutionInfo);
	}

Which forces a change to ResolutionContext

public class ResolutionContext
{
	private readonly ResolutionInfo _resolutionInfo;

	public ResolutionContext(ResolutionInfo resolutionInfo)
	{
		_resolutionInfo = resolutionInfo;
	}
}

Then run the tests to make sure they still pass. Next we’ll drive out the DSL support for AsSingleton(). First a failing test:

[TestFixture]
public class When_asked_to_update_the_resolution_info
{
	[Test]
	public void Given_a_request_to_make_it_singleton()
	{
		Test.Static()
			.When(asked_to_make_it_a_singleton)
			.With(non_singleton_resolution_info)
			.With(context_containing_the_resolution_info)
			.Should(configure_the_resolution_info_to_be_singleton)
			.Should(return_the_context)
			.Verify();
	}
}

the test implementation details:

	private void asked_to_make_it_a_singleton()
	{
		_result = _context.AsSingleton();
	}

	private void configure_the_resolution_info_to_be_singleton()
	{
		_resolutionInfo.IsSingleton.ShouldBeTrue();
	}

	private void context_containing_the_resolution_info()
	{
		_context = new ResolutionContext(_resolutionInfo);
	}

	private void non_singleton_resolution_info()
	{
		_resolutionInfo = new ResolutionInfo
			{
				IsSingleton = false
			};
	}

	private void return_the_context()
	{
		ReferenceEquals(_result, _context).ShouldBeTrue();
	}

which forces the following changes to make it compile:

public class ResolutionInfo
{
	public MulticastDelegate Initializer { get; set; }
	public bool IsSingleton { get; set; }
}

public class ResolutionContext
{
//	... constructor

	public ResolutionContext AsSingleton()
	{
		return null;
	}
}

red

STEP 1: non singleton resolution info
STEP 2: context containing the resolution info
WHEN asked to make it a singleton
SHOULD configure the resolution info to be singleton - FAILED

fix it

public class ResolutionContext
{
//	... constructor

	public ResolutionContext AsSingleton()
	{
		_resolutionInfo.IsSingleton = true;
		return null;
	}
}

run the test again and get red again

STEP 1: non singleton resolution info
STEP 2: context containing the resolution info
WHEN asked to make it a singleton
SHOULD configure the resolution info to be singleton - PASSED
SHOULD return the context - FAILED

fix it

public class ResolutionContext
{
//	... constructor

	public ResolutionContext AsSingleton()
	{
		_resolutionInfo.IsSingleton = true;
		return this;
	}
}

green. Now to implement the functionality we started with at the beginning of this post. First the failing test:

public class When_asked_to_get_an_object_instance
{
	[Test]
	public void Given_a_type_that_has_been_configured_as_a_singleton()
	{
		Test.Static()
			.When(asked_to_get_an_instance)
			.With(a_type_that_has_been_configured_as_a_singleton)
			.Should(get_a_non_null_instance)
			.Should(get_the_same_instance_every_time)
			.Verify();
	}

and the test implementation details

	private void a_type_that_has_been_configured_as_a_singleton()
	{
		LeafIoC.Configure<IComparable, Int32>()
			.AsSingleton();
		_getInstance = () => LeafIoC.GetInstance<IComparable>();
	}

	private void get_the_same_instance_every_time()
	{
		var other = _getInstance();
		ReferenceEquals(_result, other).ShouldBeTrue();
	}

red

STEP 1: a type that has been configured as a singleton
WHEN asked to get an instance
SHOULD get a non null instance - PASSED
SHOULD get the same instance every time - FAILED

To implement this we’re going to have to store the instance once we create it which implies a change to the ResolutionInfo.

public class ResolutionInfo
{
	public MulticastDelegate Initializer { get; set; }
	public bool IsSingleton { get; set; }
	public object Instance { get; set; }
}

Then the change to store the instance if singleton was configured and return the instance if it exists:

	public static TInterface GetInstance<TInterface>()
	{
		var type = typeof(TInterface);
		ResolutionInfo info;
		if (!Configuration.TryGetValue(type, out info))
		{
			throw new ConfigurationErrorsException(string.Format("No instance of {0} has been configured.", type.FullName));
		}

		if (info.Instance != null)
		{
			return (TInterface)info.Instance;
		}

		var instance = ((Func<TInterface>)(info.Initializer)).Invoke();
		if (info.IsSingleton)
		{
			info.Instance = instance;
		}
		return instance;
	}

green.

Are we done? Maybe. Since the container is static the only way it is going to be disposed is if the application is exiting. My understanding is we don’t have to write a finalizer or implement IDisposable on the container because the system will make sure the finalizers get called on all objects.

What about when Reset is called? The system isn’t shutting down but the user wants a fresh start. Who is responsible for making sure the object gets disposed? The user doesn’t expect to have to dispose a singleton so it looks like we’ll have to take responsibility for making sure dispose is called on our singletons when Reset is called.

public class When_asked_to_reset
{
	[Test]
	public void Given_instances_of_disposable_types_exist()
	{
		Test.Static()
			.When(asked_to_reset)
			.With(instances_of_disposable_types_exist)
			.Should(dispose_of_any_disposable_instances)
			.Should(remove_all_exising_configurations)
			.Verify();
	}

	private static void dispose_of_any_disposable_instances()
	{
		((DisposeTester)_instance).Disposed.ShouldBeTrue();
	}

	private static void instances_of_disposable_types_exist()
	{
		LeafIoC.Configure<IDisposable, DisposeTester>().AsSingleton();
		_instance = LeafIoC.GetInstance<IDisposable>();
	}

	public class DisposeTester : IDisposable
	{
		public bool Disposed { get; private set; }
		public void Dispose()
		{
			Disposed = true;
		}
	}

red

STEP 1: instances of disposable types exist
WHEN asked to reset
SHOULD dispose of any disposable instances - FAILED

fix it

	public static void Reset()
	{
		DisposeDisposableInstances();
		Configuration.Clear();
	}

	private static void DisposeDisposableInstances()
	{
		foreach (var resolutionInfo in Configuration.Values)
		{
			var instance = (IDisposable)resolutionInfo.Instance;
			resolutionInfo.Instance = null;
			instance.Dispose();
		}
	}

green. Next test:

	public class NonDisposableTester : IComparable
	{
		public int CompareTo(object obj)
		{
			throw new NotImplementedException();
		}
	}

	[Test]
	public void Given_instances_of_non_disposable_types_exist()
	{
		Test.Static()
			.When(asked_to_reset)
			.With(instances_of_non_disposable_types_exist)
			.Should(remove_all_exising_configurations)
			.Verify();
	}

	private static void instances_of_non_disposable_types_exist()
	{
		LeafIoC.Configure<IComparable, NonDisposableTester>().AsSingleton();
		_instance = LeafIoC.GetInstance<IComparable>();
	}

red

STEP 1: instances of non disposable types exist
WHEN asked to reset - FAILED

System.InvalidCastException : Unable to cast object of type 'NonDisposableTester' to type 'System.IDisposable'.

fix it

	private static void DisposeDisposableInstances()
	{
		foreach (var resolutionInfo in Configuration.Values)
		{
			var instance = resolutionInfo.Instance as IDisposable;
			if (instance == null)
			{
				continue;
			}
			resolutionInfo.Instance = null;
			instance.Dispose();
		}
	}

Go on to the next post: implementing named instances

The code for this project is available from github.

Read Full Post »

The gist of BDD (Behavior Driven Develoment) is being able to write tests in language that a non-programmer can read and verify. Until now I’ve used a style that I adapted from J P Boodhoo that looks like the following:

An outer class for all tests of a particular class

public class CollectionTests
{
}

that contains TestFixture classes for each action (method) on the class under test

	[TestFixture]
	public class When_asked_to_Save
	{
	}

	[TestFixture]
	public class When_asked_to_Delete
	{
	}

each of which contains test methods that describe the desired behavior when its action is called with certain inputs.

	[Test]
	public void Should_set_the_Id_if_save_succeeds()
	{
	}

	[Test]
	public void Should_return_an_error_if_save_fails()
	{
	}

This makes tests that are highly readable in NUnit with test run results that can be coerced into a Word or Excel report. The main problem is the bodies of the Setup TearDown and particularly the Test methods get so cluttered with Mock expectations and other goo that even developers can have a hard time looking at them and verifying their correctness not to mention fixing breakage.

	var person = new Person
	{
		FirstName = "Loreena",
		LastName = "McKennitt"
	};
	_people.Save(person);
	person.Id.ShouldNotBeNull();

Did Save() succeed? Was it supposed to?

Then Jon Kruger sent out this tweet describing his desired test framework and sent me down this really interesting path:

	Given(person)
	.With(valid_first_name)
	.With(valid_last_name)
	.When(save_succeeds)
	.Should(populate_the_id_property)

So let’s figure out how to do it.

The first thing is we don’t want to have to inherit from a base class to get access to Given() so that implies some kind of starter class. It could be an extension method, or a class that has to be new’d up, but I prefer a static class that starts a DSL for type T.

public static class Test
{
	public static TestDefinition<T> For<T>()
	{
		return new TestDefinition<T>();
	}
}

Next we add the Given method to TestDefinition to capture an instance of the class under test. In most cases this will end up being a Mock. We’ll just pass the T along and return a new container.

public class TestDefinition<T>
{
	public TestGivenClause<T> Given(T item)
	{
		return new TestGivenClause<T>(item);
	}
}

Given() has two possible next paths With() – a method that, according to the example, can be called multiple times to modify the item under test

	public TestGivenClause<T> With(...)
	{
		// ...
		return this;
	}

and When() which transitions us to the next stage in the DSL

	public TestWhenClause<T> When(...)
	{
		return new TestWhenClause<T>(...);
	}

The interesting question is what goes in the …’s? Well, notice when looking at the desired language

	Given(person)
	.With(valid_first_name)
	.With(valid_last_name)
	.When(save_succeeds)
	.Should(populate_the_id_property)

that the things passed to With(), When() and Should() don’t have parentheses… and they aren’t lambdas either. If we want this to exactly match the example the only things left are Func and Action. In the case of With() we’re going to want to take a T and make a change to it but we don’t need to return anything so that implies Action. Based on the example we also want to be able to call With() multiple times so internally we’ll store the Actions in a List:

	private readonly List<Action<T>> _setupActionsForItem = new List<Action<T>>();

	public TestGivenClause<T> With(Action<T> setupForItem)
	{
		_setupActionsForItem.Add(setupForItem);
		return this;
	}

When() can also take an action because our implementation of save_succeeds can store its result in our test class for later processing, so:

	public TestWhenClause<T> When(Action<T> action)
	{
		return new TestWhenClause<T>(_item, action, _setupActionsForItem);
	}

Which implies the constructor:

	public TestWhenClause(
		T item, 
		Action<T> action, 
		List<Action<T>> setupActionsForItem)
	{
		_item = item;
		_action = action;
		_setupActionsForItem = setupActionsForItem;
	}

Now the example shows only one Should() clause but we’ll want that to be repeatable too (although in practice I generally only have one assertion per test method) so that means the implementation is just a variation on With()

	public TestWhenClause<T> Should(Action<T> verify)
	{
		_verificationActions.Add(verify);
		return this;
	}

The only thing remaining is a command to fire off the test. Let’s call it Verify() . In Verify() we’ll have to perform all the With() actions on the class under test

	public void Verify()
	{
		foreach (var setupItem in _setupActionsForItem)
		{
			setupItem(_item);
		}

then perform the When() action

		_action(_item);

and finally, perform all the Should() verifications

		foreach (var verify in _verificationActions)
		{
			verify(_item);
		}
	}

Does that look familiar? It should. We’ve just written a wrapper around the AAA (Arrange Act Assert) pattern that lets us abstract all the details out of the actual test.

Now to use it we write the test in language as close to business-speak as possible. The first difference is we’re going to be testing a Repository, not a Person (because we’re separating concerns) so the test comes out as:

[Test]
public void Should_populate_the_person_id_if_save_succeeds()
{
	Test.For<MongoCollection<Person>>()
		.Given(_personRepository)
		.When(Save_succeeds)
		.Should(Populate_the_id_property_of_the_person)
		.Verify();
}

Now that we can see the final product the .For<T> part is a bit icky to look at and certainly wouldn’t be useful to a business person. Let’s clean that up a bit. We can get the T by induction if we just pass the object so we’ll add Given() to Test too:

	public static TestGivenClause<T> Given<T>(T item)
	{
		return new TestGivenClause<T>(item);
	}

and that will make the repository test look like

[Test]
public void Should_populate_the_person_id_if_save_succeeds()
{
	Test.Given(_personRepository)
		.When(Save_succeeds)
		.Should(Populate_the_id_property_of_the_person)
		.Verify();
}

Much better.

Next we’ll use Resharper (or whatever) to stub the Action methods and finally fill in the details

private void Save_succeeds(MongoCollection<Person> repository)
{
	_person = new Person
	          	{
	          		FirstName = "Loreena",
	          		LastName = "McKennitt"
	          	};

	repository.Save(_person);
}

private void Populate_the_id_property_of_the_person(MongoCollection<Person> repository)
{
	_person.Id.ShouldNotBeNull();
}

That was too easy. Let’s try another one:

[Test]
public void Should_not_populate_the_person_id_if_save_fails()
{
	Test.Given(_personRepository)
		.When(Save_fails)
		.Should(Not_populate_the_id_property_of_the_person)
		.Verify();
}

private void Save_fails(MongoCollection<Person> repository)
{
	_person = new Person
	          	{
	          		FirstName = null,
	          		LastName = null
	          	};

	Assert.Throws<ArgumentException>(()=> repository.Save(_person));
}

private void Not_populate_the_id_property_of_the_person(MongoCollection<Person> repository)
{
	_person.Id.ShouldBeNull();
}

Sweet.

This framework has the side benefit of forcing us to break up our Actions into composable chunks and keeps our test classes moist and maintainable. Now one could go so far as to split the parts among the [Setup] (For, Given, and When) [Test] (Should) and [TearDown] (Verify) but I think that takes us away from the goal – tests that are so easy to read that a non-developer can understand and verify their correctness and detect/suggest missing test cases.

One more thing to think about: Given a project where many tests are written in this style it wouldn’t be too difficult to write a tool to crawl the source code for methods marked [Test] that start with the Test.Given() construct, grab their innards and drop them all in some kind of document that could be reviewed by an analyst. Hmmm.

This framework is available as part of the FluentAssert project on GitHub.

Edit: Try any of my BDD Kata if you’d like to see the current version of this framework in action.

Read Full Post »

%d bloggers like this: