Project Description
A Behavior Driven Design (BDD) library adding a clearer syntax and better organization to unit tests. Currently tested working with NUnit and MSTest.

Quick Start
1) Download the pre-compiled assembly and add a reference to it from your test project.

2) Create a new class deriving from Behavioral.UnitTest<TTarget> or Behavioral.UnitTest<TTarget, TReturnType>. The latter is required for testing methods that return a type, ie: are not void.

3) In the class' constructor, call GivenThat<TInitializer>(), When<TAction>() and Then<TAssertion>(), specifying English-language sentences (in Pascal Case) for the type arguments.

4) Define the classes in part 3, implementing IInitializer<TTarget>, IAction<TTarget> and IAssertion<TTarget>, respectively.

Example

Here is a very simple example of how to use Behavioral - in conjunction with NUnit - to test the Add() method of a simple Calculator class.

CalculatorTests.cs

using Behavioral;
using NUnit;

namespace MyTests
{
	[TestFixture]
	public class AddingTwoPositiveNumbersShouldGiveCorrectResult : UnitTest<Calculator, int>
	{
		[Test]
		public override void Run()
		{
			GivenThat<CalculatorIsDefaultConstructed>();

			When<TwoNumbersAreAdded>(13, 45);

			Then<ResultShouldMatch>(58);
		}
	} 

	[TestFixture]
	public class AddingNumbersThatCausesOverflowShouldThrowOverflowException : UnitTest<Calculator, int>
	{
		[Test]
		public override void Run()
		{
			GivenThat<CalculatorIsDefaultConstructed>();

			When<TwoNumbersAreAdded>(int.MaxValue, 1);

			ThenThrow<OverflowException>();
		}
	}
}


CalculatorInitializers.cs

public class CalculatorIsDefaultConstructed : IInitializer<Calculator>
{
	public void SetUp(ref Calculator calculator)
	{
		calculator = new Calculator();
	}
}


CalculatorActions.cs

public class TwoNumbersAreAdded : IAction<Calculator, int>
{
	public TwoNumbersAreAdded(int x, int y)
	{
		this.x = x;
		this.y = y;
	}

	public int Execute(Calculator calculator)
	{
		return calculator.Add(this.x, this.y);
	}

	private int x;
	private int y;
}


CalculatorAssertions.cs

public class ResultShouldMatch : IAssertion<Calculator, int>
{
	public ResultShouldMatch(int expectedValue)
	{
		this.expectedValue;
	}

	public void Verify(Calculator calculator, int returnValue)
	{
		Assert.AreEqual(this.expectedValue, returnValue);
	}

	private int expectedValue;
}


So, that's a simple example of how to trivially test the following Calculator implementation:

Calculator.cs

public class Calculator
{
	public int Add(int x, int y)
	{
		checked
		{
			return x + y;
		}
	}
}



Background
Behavior Driven Development (BDD) is the natural next step after Test Driven Development (TDD). BDD is many different things, but one aspect of BDD is addressed by Behavioral: unit test organization.

The usual way of organizing unit tests is as follows:

[TestFixture]
public class CalculatorFixture
{
	[SetUp]
	public void SetUp()
	{
		this.calculator = new Calculator();
	}

	[Test]
	public void CanAddTwoPositiveNumbers()
	{
		int result = this.calculator.Add(13, 45);
		Assert.AreEqual(58, result);
	}

	[Test]
	public void AdditionOverflowCausesException()
	{
		Assert.Throws<OverflowException>(() => this.calculator.Add(int.MaxValue, 1));
	}

	private Calculator calculator;
}


As the tests become more complex and involved, there are two problems with this approach that are addressed by Behavioral.
  • The tests do not promote reuse, neither of the initialization code, the action under test nor the assertions that are made after the fact.
  • The tests can become hard to understand and, as the tests in an agile project form a reliable documentation of the code's intent, it is important to keep them simple.

Last edited Aug 15, 2011 at 3:10 PM by garymcleanhall, version 13