October 2005 Entries

Data Driven Unit Tests in .NET 2.0

 

The new unit testing namespace provided with .NET 2.0 includes support for data-driven testing which is a great help when writing unit tests that deal with data access, or at least database access as it appears. The framework provides the DataSourceAttribute to specify your data source (fairly obvious), however, this attribute is really only designed for database access. What if my data source were an XML document or a 3rd party web service? Obviously there are other ways to access an XML document or a web service, such as through the Properties property of the TestContext. I'm just surprised the attribute wasn't called DatabaseAttribute. Thoughts?

Design by Contract with AOP Revisited

 

Last night I posted a quick example of how we could place runtime requirements on methods. This is a common need by developers. I have since updated my example to clean it up a bit and make it more useable. The cleanup should make it much more extensible. In this example, I am placing two restrictions on our parameter. In order to call our function, the parameter being passed to it must be within both the minimum and maximum range defined in our PrologAttribute. What are your thoughts?

 

using System;
using
System.Reflection;
using
AopAlliance.Intercept;
using
Spring.Aop.Framework;
using
Spring.Aop.Interceptor;
using
Microsoft.VisualStudio.TestTools.UnitTesting;

namespace
DeveloperNotes
{
public class ConstraintInterceptor : IMethodInterceptor
{
public object Invoke(IMethodInvocation invocation)
{
MethodInfo mi
= invocation.Method;
if
(mi != null)
{
Object[] attrs
= mi.GetCustomAttributes(typeof(PrologAttribute), true);
if
(attrs != null && attrs.Length > 0)
{
bool results = false;
foreach
(PrologAttribute pl in attrs)
{
if (pl != null)
{
IEvaluator eval
= pl.Evaluator as IEvaluator;
if
(eval != null)
{
object[] args = invocation.Arguments;
results = eval.Evaluate(args[pl.Index]);
if
(!results)
break;
else
continue;
}
}
}
if(results)
return invocation.Proceed();
else
return null;
}
else
{
return invocation.Proceed();
}
}
return null;
}
}

public interface IEvaluator
{
bool Evaluate(object arg);
}

[AttributeUsage(AttributeTargets.Method, AllowMultiple
=true, Inherited=true)]
public class PrologAttribute : Attribute
{
private int index;
public
IEvaluator evaluator;

public
PrologAttribute() { }

public PrologAttribute(int index, Type evalType, params object[] args)
{
this.Index = index;
Object obj = Activator.CreateInstance(evalType, args);
if
(obj != null)
this.evaluator = obj as IEvaluator;
}

public IEvaluator Evaluator
{
get { return evaluator; }
set { evaluator = value; }
}

public int Index
{
get { return index; }
set { index = value; }
}

public bool Evaluate(object arg)
{
return evaluator.Evaluate(arg);
}
}

public class StringLengthEvaluator : IEvaluator
{
private int min = 0;
private int
max = 0;

public
StringLengthEvaluator(int min, int max)
{
this.min = min;
this
.max = max;
}

public bool Evaluate(object arg)
{
int len = arg.ToString().Length;
return
(len <= max) && (len >= min);
}
}

public class ObjectFactory
{
public static K CreateObject<T, K>()
{
T obj
= Activator.CreateInstance<T>();
if
(obj != null)
{
ProxyFactory factory
= new ProxyFactory(obj);
factory.AddAdvice(new ConstraintInterceptor());
return
(K)factory.GetProxy();
}
return default(K);
}
}

public interface IPerson
{
void SayHello(string name);
}

public class Person : IPerson
{

[Prolog(
0, typeof(StringLengthEvaluator), 0, 5)]
public void SayHello(string name)
{
Console.WriteLine(
"Hello {0}", name);
}
}

class Program
{
static void Main(string[] args)
{
IPerson p
= ObjectFactory.CreateObject<Person, IPerson>();
if
(p != null)
{
p.SayHello(
"Nicholas");
p.SayHello("Nick");
}
Console.Read()
;
}
}
}

Design by Contract with AOP

 

Over a year ago I discussed with Joe Duffy, a program manager on the CLR team about the concept of design by contract. Recently I had a similar disucussion with Richard Hale Shaw and Cyrus Najmabadi a developer on the C# compiler team at Microsoft while I was at the MVP Global Summit. I thought I would put together a quick example of how this could be applied, spawning off of my previous post discussing AOP. This example allows the developer to place certain restrictions on a method and for those restrictions to be applied before the method is actually called. In this example, if those restrictions aren't meet, the method is not called. If you step through the code, you will notice the second call to SayHello does not get called. Since this is simply an example, there is a lot that could change with this. For one, we would want to include the concept of an EpilogAttribute which would allow the developer to place constraints on the return value of the method as well. I'm curious what others think of something like this?

 

using System;
using
System.Reflection;
using
AopAlliance.Intercept;
using
Spring.Aop.Framework;
using
Spring.Aop.Interceptor;
using
Microsoft.VisualStudio.TestTools.UnitTesting;

namespace
DeveloperNotes
{
public class ConstraintInterceptor : IMethodInterceptor
{
public object Invoke(IMethodInvocation invocation)
{
MethodInfo mi
= invocation.Method;
if
(mi != null)
{
Object[] attrs
= mi.GetCustomAttributes(typeof(PrologAttribute), true);
if
(attrs != null && attrs.Length > 0)
{
PrologAttribute pl
= attrs[0] as PrologAttribute;
if
(pl != null)
{
object[] args = invocation.Arguments;
if
(args != null)
{
bool results = !Assert.Equals(args[pl.Index], pl.RVal);
if
(results)
return invocation.Proceed();
else
return null;
}
}
}
else
{
return invocation.Proceed();
}
}
return null;
}
}

public class PrologAttribute : Attribute
{
public int Index;
public object
RVal;

public
PrologAttribute() { }

public PrologAttribute(int index, object rval)
{
this.RVal = rval;
}
}

public class ObjectFactory
{
public static K CreateObject()
{
T obj
= Activator.CreateInstance();
if
(obj != null)
{
ProxyFactory factory
= new ProxyFactory(obj);
factory.AddAdvice(new ConstraintInterceptor());
return
(K)factory.GetProxy();
}
return default(K);
}
}

public interface IPerson
{
void SayHello(string name);
}

public class Person : IPerson
{

[Prolog(
0, "")]
public void SayHello(string name)
{
Console.WriteLine(
"Hello {0}", name);
}
}

class Program
{
static void Main(string[] args)
{
IPerson p
= ObjectFactory.CreateObject();
if
(p != null)
{
p.SayHello(
"Nick");
p.SayHello("Nicholas");
}
Console.Read()
;
}
}
}

CTP's Everywhere

 

If you really enjoy trying out new Community Technology Previews (CTP's), but have found the crux to be having the correct corresponding packages installed at the same time, Channel 9 is to the rescue. Brad Abrams and Jason Sutherland have put together an XML file that will provide you with a list of all components that will play well together based on what you want to install. Channel 9 has put together a nice little interface to it. Check it out.