After asking this question about implementing an aspect with PostSharp, it came to my mind that I might have to update the code of this aspect in the future, and that I did not want to take the risk of breaking everything afterwards.
So, I started thinking about unit testing.
My first question is:
Is it relevant to think about unit testing an aspect?
I would like the answer to be "yes", but if not, I expect getting other advices.
And then, if so,
How to implement unit testing for PostSharp aspects?
Yes it certainly makes sense to unit tests aspects, since they represent functionality and since you are using it in more than one place it is even more important to test it.
However you have to devide this into two parts:
For the first part, if you have decoupled the actual functionality from the attribute that executes the aspect functionality properly, creating unit tests should be no different than unit testing normal code.
For the second part you need to decouple the extraction of context as well, this might seem like overkill but if you wanne unit test it properly you will need todo it I'm afraid.
On that note you should also make use of compile time validation, which can also prevents you from using the attributes in the wrong way. Sometimes it's necessary to test certain conditions that you can not describe with the Attribute syntax, then compile time validation comes into play. This has been a great asset for me and reduced the amount of debugging sessions in regards to PostSharp aspects significantly, see:
http://www.sharpcrafters.com/postsharp/robustness
Here is some very basic sample code, no DI nothing, just to illustrate how to split things up:
public sealed class TraceAttribute : OnMethodBoundaryAspect
{
private readonly string category;
private TraceArgumentService argumentService;
private TraceService traceService;
public string Category { get { return category; } }
public TraceAttribute(string category)
{
this.category = category;
}
public override void RuntimeInitialize(System.Reflection.MethodBase method)
{
base.RuntimeInitialize(method);
this.argumentService = new TraceArgumentService();
this.traceService = new TraceService();
}
public override void OnEntry(MethodExecutionArgs args)
{
traceService.Write(
argumentService.GetDeclaringTypeName(args),
argumentService.GetMethodName(args),
category);
}
}
public class TraceArgumentService
{
public string GetDeclaringTypeName(MethodExecutionArgs args)
{
return args.Method.DeclaringType.Name;
}
public string GetMethodName(MethodExecutionArgs args)
{
return args.Method.Name;
}
}
public class TraceService
{
public void Write(string declaringTypeName, string methodName, string category)
{
Trace.WriteLine(string.Format("Entering {0}.{1}.",
declaringTypeName, methodName), category);
}
}
You might ask why TraceService
and a separate TraceArgumentService
:
MethodExecutionArgs
.MethodExecutionArgs
is not part of Tracing, it's more related to the aspect. Since you want to be able to test it, you need to somehow separate it.