//
// Copy freely and buy me a beer!
//
// Mikael Lundin
// mikael.lundin@LiteMedia.se
// 2009-03-24
// This UnitTestDataSource will help you to run your unit tests in a web application and validate the result.
namespace Litemedia.Utils.Web
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Web.UI;
using NUnit.Framework;
///
/// This datasource control may be put on any ASPX page and serve as a datasource for other controls
///
public class UnitTestDataSource : DataSourceControl
{
///
/// Gets or sets the name of the assembly containing the tests
///
/// The name of the assembly.
public string AssemblyName { get; set; }
///
/// Gets the named data source view associated with the data source control.
///
/// The name of the to retrieve. In data source controls that support only one view, such as , this parameter is ignored.
///
/// Returns the named associated with the .
///
protected override DataSourceView GetView(string viewName)
{
return new NUnitTestDataSourceView(this, viewName, this.AssemblyName);
}
}
///
/// This is a NUnit test runner, that will find all test methods in an assembly and run them
///
public class NUnitTestDataSourceView : DataSourceView
{
///
/// This is the assembly that contains the unit tests
///
private Assembly assembly;
///
/// Initializes a new instance of the class.
///
/// The datasource owner
/// Name of the view.
/// Name of the assembly where the tests are located
public NUnitTestDataSourceView(IDataSource owner, string viewName, string assemblyName)
: base(owner, viewName)
{
this.assembly = Assembly.Load(assemblyName);
}
///
/// Gets a list of data from the underlying data storage.
///
/// A that is used to request operations on the data beyond basic data retrieval.
///
/// An list of data from the underlying data storage.
///
protected override IEnumerable ExecuteSelect(DataSourceSelectArguments arguments)
{
List result = new List();
foreach (Type type in this.GetAllTestFixtures())
{
object instance = Activator.CreateInstance(type);
foreach (var method in this.GetAllMethods(type))
{
string name = string.Format("{0}.{1}.{2}()", type.Namespace, type.Name, method.Name);
try
{
if (this.HasAttribute(method, typeof(IgnoreAttribute)))
{
continue; // IgnoreAttribute, casues this test not to rune
}
method.Invoke(instance, new object[] { });
result.Add(new UnitTestResult(name)); // Success!
}
catch (Exception outerException)
{
Exception innerException = outerException.InnerException;
if (this.IsExpectedException(method, innerException))
{
result.Add(new UnitTestResult(name)); // Success!
}
else
{
result.Add(new UnitTestResult(name, innerException)); // Failure!
}
}
}
}
return result;
}
///
/// Determines whether the specified member has attribute.
///
/// The member.
/// Type of the attribute.
///
/// true if the specified member has attribute; otherwise, false.
///
private bool HasAttribute(MemberInfo member, Type attributeType)
{
return member.GetCustomAttributes(attributeType, true).Length > 0;
}
///
/// Gets all test fixtures in the assembly.
///
/// A list of types that are marked with the TestFixtureAttribute
private IEnumerable GetAllTestFixtures()
{
IList types = new List();
foreach (Type type in this.assembly.GetTypes())
{
if (this.HasAttribute(type, typeof(TestFixtureAttribute)))
{
types.Add(type);
}
}
return types;
}
///
/// Gets all methods with the TestAttribute
///
/// The type in where we will find the methods
/// A list of MethodInfos that are marked with the TestAttribute
private IEnumerable GetAllMethods(Type type)
{
IList methods = new List();
foreach (MethodInfo method in type.GetMethods())
{
if (this.HasAttribute(method, typeof(TestAttribute)))
{
methods.Add(method);
}
}
return methods;
}
///
/// Determines whether [is expected exception] [the specified method].
///
/// The method that threw the exception
/// The exception that was thrown
///
/// true if [is expected exception] [the specified method]; otherwise, false.
///
private bool IsExpectedException(MethodInfo method, Exception e)
{
ExpectedExceptionAttribute attribute = null;
object[] methodAttributes = method.GetCustomAttributes(typeof(ExpectedExceptionAttribute), true);
// ExpectedAttribute exists
if (methodAttributes.Length > 0)
{
attribute = (ExpectedExceptionAttribute)methodAttributes[0];
}
// ExpectedAttribute is for thrown exception
return attribute != null && (attribute.ExceptionName == e.GetType().Name || attribute.ExceptionType == e.GetType() || attribute.ExpectedMessage == e.Message);
}
}
///
/// The result of a UnitTest method
///
public class UnitTestResult
{
///
/// Initializes a new instance of the class. The test run was success.
///
/// The name of the method
public UnitTestResult(string name)
: this(name, null)
{
this.Success = true;
}
///
/// Initializes a new instance of the class. The test run was a failure.
///
/// The name of the test method
/// The exception that was thrown from the test
public UnitTestResult(string name, Exception exception)
{
this.Name = name;
this.Exception = exception;
}
///
/// Gets the name of the test method.
///
/// The name of the test method
public string Name { get; private set; }
///
/// Gets a value indicating whether this is success or not.
///
/// true if success; otherwise, false.
public bool Success { get; private set; }
///
/// Gets the exception thrown from the test method
///
/// The exception.
public Exception Exception { get; private set; }
///
/// Returns a that represents the current .
///
///
/// A that represents the current .
///
public override string ToString()
{
return this.Name;
}
}
}