Written December 22, 2008 at 14:29 MST Tagged c sharp and programming
We left off in part 1 with an example of a test written using my current style and I finished off outlining the skeleton for one of the base classes that shields the actual Concern classes from a lot of noisy test related nomenclature.
In this post I am going to break down the responsibilities of the “an_observations_basic_set_of_behaviours” class. For a quick reminder, here is the skeleton outline of the class:
[Observations]
public abstract class an_observations_set_of_basic_behaviours
{
static protected IDictionary<Type, object> dependencies;
static Exception exception_thrown_while_the_sut_performed_its_work;
static protected Action behaviour_of_the_sut;
[SetUp]
public void setup() {}
void prepare_to_make_an_observation() {}
[TearDown]
public void tear_down() {}
after_all_observations a = () => dependencies.Clear();
ICommand build_command_chain void run_action protected virtual void initialize_the_sut() {} static public void doing(Action action) {} static protected Exception exception_thrown_by_the_sut { get { } } static Exception get_exception_throw_by(Action action) {} static protected object an(Type type) {} static protected InterfaceType an } }
static protected IDictionary<Type, object> dependencies;
static Exception exception_thrown_while_the_sut_performed_its_work;
static protected Action behaviour_of_the_sut;
That takes care of the first set of fields, lets move on to a method that should look very familiar to MbUnit/NUnit people:
[SetUp]
public void setup()
{
exception_thrown_while_the_sut_performed_its_work = null;
dependencies = new Dictionary<Type, object>();
prepare_to_make_an_observation();
}
This is a traditional MbUnit setup method. Because it is decorated with the SetUp attribute, all of the code in this method will get run once before each observation (test). Essentially this method is used to reset stateful fields, this is not that interesting. The guts of setup happen in the following method:
void prepare_to_make_an_observationjjj()
{
run_action<context>();
initialize_the_sut();
run_action<after_the_sut_has_been_initialized>();
run_action<because>();
}
void run_action
public delegate void context();
public delegate void after_the_sut_has_been_initialized();
public delegate void because();
public delegate void after_each_observation();
You may be wondering why I did not just use the plain old action delegate as opposed to creating discrete delegate types. This is so the driver class (this one we are examining) has a clearer way of differentiation between the different delegates. Without the named delegate types, I would have to resort to following a convention for the name I assigned to a field for an Action delegate (messy, messy).
Here is the full implementation of the run_action method:
void run_action { build_command_chain }
Once again, this method is pretty simple as it is really delegating its responsibility to whatever gets created by the build_command_chain method. So lets dive into the full body of this method, as this is really where the majority of the work is being done:
ICommand build_command_chain { var actions = new Stack<ICommand>(); var current_class = GetType(); while (current_class.is_a_concern_type()) { actions.Push(new DelegateFieldInvocation(typeof (DelegateType), this, current_class)); current_class = current_class.BaseType; } return actions.as_command_chain(); }
So what is going on here. Take a look at this test one more time as it will give you a good visual as to what is going on:
[Concern(typeof (MappingStep<,,>))]
public class when_an_expression_mapping_step_is_told_to_run :
concern_for_mapping_step
{
static SomeSourceObject item;
static SomeDestinationObject destination;
static string name;
context c = () =>
{
item = new SomeSourceObject();
name = "JP";
destination = new SomeDestinationObject();
source_evaluator.Stub(x => x.evaluate_against(item)).Return(name);
};
because b = () => sut.map(item, destination);
[Observation]
public void should_run_the_target_evaluator_passing_it_the_information_retrieved_from_evaluating_the_source()
{
target_action.was_told_to(x => x.act_against(destination, name));
}
}
context c = () =>
{
target_action = the_dependency<ITargetAction<SomeDestinationObject, string>>();
source_evaluator = the_dependency<ISourceEvaluator<SomeSourceObject, string>>();
};
It is important to remember that the “an_observations_basic_set_of_behaviours” class is the test driver. Here is the inheritance hierarchy for this current test fixture:
public interface ICommand
{
void run();
}
This is a pretty stock interface that I have used for years. The uses are endless!! How does the loop know when to end? What is with the following method?:
current_class.is_a_concern_type()
The field current_class is of type Type. The is_a_concern_type method is a local extension method that has the following implementation:
static public bool is_a_concern_type(this Type type)
{
return typeof (an_observations_set_of_basic_behaviours)
.IsAssignableFrom(type);
}
This method just adds a bit of readability (from the point of usage, which is a huge bonus of extension methods) and ensures that we wont worry about putting anything that is not either a derivative of an_observations_set_of_basic_behaviours or the an_observations_set_of_basic_behaviours type itself on the stack (it will always be the top item on the stack at the end of the traversal).
So lets explore the line that does the grunt work:
actions.Push(new DelegateFieldInvocation(typeof (DelegateType), this, current_class));
Keep in mind that the actions field is a stack of ICommand. It stands to reason that the DelegateFieldInvocation class is an ICommand implementation. Which it is. The DelegateFieldInvocation class takes in its constuctor a delegate type to scour for, an instance to act against (always this), and finally the particular type to reflect against (this will change as we continue to walk up the inheritance hierarchy). Here is the implementation of the DelegateFieldInvocation:
public class DelegateFieldInvocation : ICommand
{
const BindingFlags probing_flags = BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly;
Type delegate_type;
readonly object instance;
IEnumerable<FieldInfo> fields;
public DelegateFieldInvocation(Type delegate_type, object instance, Type current_type)
{
fields = current_type.GetFields(probing_flags);
this.delegate_type = delegate_type;
this.instance = instance;
}
public void run()
{
all_fields_of_the_target_delegate_type().each(x => x.GetValue(instance).downcast_to<Delegate>().DynamicInvoke());
}
IEnumerable<FieldInfo> all_fields_of_the_target_delegate_type()
{
return fields.Where(x => x.FieldType.Equals(delegate_type));
}
}
All this command does in its run implementation is scour all of the fields in the “current_type” and filters them to look for only fields of the certain delegate type being searched for. Once the particular delegate type is found, we get the value of the field using the instance:
x.GetValue(instance).downcast_to<Delegate>()
return actions.as_command_chain();
All that this method does is return a Composite of commands that when told to run, will run each of the commands that it is composed with (we’ll dive into that at a later date). So back up in the run_action method:
build_command_chain
It should now be clear to see that the run() method is invoked on the Composite, that will cause all of the commands to run in sequence. In the case of run_action void prepare_to_make_an_observation() { run_action<context>(); initialize_the_sut(); run_action<after_the_sut_has_been_initialized>(); run_action<because>(); } because b = () => sut.map(item, destination);
For clarification, look again at how the run_action method is used:
The other calls to run action simply walk down the hierarchy invoking the appropriate delegate fields that may or may not be present in the actual test classes. The because block is the behaviour we are invoking of our system under test. In the case of the test we first examined:
For the design pattern aware, you will have already realized that the prepare_to_make_an_observation method (along with the use of delegate fields that can be defined at any level of the hierarchy) is just a specialization of the template method pattern, the main difference being that subclasses do not provide their behaviour by overriding abstract methods, rather they just simply have to define fields of a certain delegate type, that contain the code that will be invoked dynamically by the base class using reflection!!
That is enough for now, we’ll carry on the drilldown in the next part!!
Develop With Passion!!
Newer: Did You Know?