Skip to content

Instantly share code, notes, and snippets.

@miklund
Created December 22, 2015 21:14
Show Gist options
  • Save miklund/59ee458eaa75fbf2973f to your computer and use it in GitHub Desktop.
Save miklund/59ee458eaa75fbf2973f to your computer and use it in GitHub Desktop.
2009-03-24 Run your unit tests on the web
Title: Run your unit tests on the web
Author: Mikael Lundin
Link: http://blog.mikaellundin.name/2009/03/24/run-your-unit-tests-on-the-web.html
<litemedia:UnitTestDataSource runat="server" ID="UnitTestDataSource" AssemblyName="LiteMedia.Mint.UnitTests" OnLoad="DataBind" />
<asp:Repeater runat="server" DataSourceID="UnitTestDataSource" OnLoad="DataBind">
<HeaderTemplate><ul></HeaderTemplate>
<ItemTemplate>
<li class="<%# GetSuccessClass(Container.DataItem) %>">
<span class="name"><%# Container.DataItem %></span>
<p class="exception <%# GetSuccessClass(Container.DataItem) %>">
<%# GetException(Container.DataItem) %>
</p>
</li>
</ItemTemplate>
<FooterTemplate></ul></FooterTemplate>
</asp:Repeater>
// <copyright file="UnitTestDataSource.cs" company="LiteMedia">
// Copy freely and buy me a beer!
// </copyright>
// <author>Mikael Lundin</author>
// <email>[email protected]</email>
// <date>2009-03-24</date>
// <summary>This UnitTestDataSource will help you to run your unit tests in a web application and validate the result.</summary>
namespace Litemedia.Utils.Web
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Web.UI;
using NUnit.Framework;
/// <summary>
/// This datasource control may be put on any ASPX page and serve as a datasource for other controls
/// </summary>
public class UnitTestDataSource : DataSourceControl
{
/// <summary>
/// Gets or sets the name of the assembly containing the tests
/// </summary>
/// <value>The name of the assembly.</value>
public string AssemblyName { get; set; }
/// <summary>
/// Gets the named data source view associated with the data source control.
/// </summary>
/// <param name="viewName">The name of the <see cref="T:System.Web.UI.DataSourceView"/> to retrieve. In data source controls that support only one view, such as <see cref="T:System.Web.UI.WebControls.SqlDataSource"/>, this parameter is ignored.</param>
/// <returns>
/// Returns the named <see cref="T:System.Web.UI.DataSourceView"/> associated with the <see cref="T:System.Web.UI.DataSourceControl"/>.
/// </returns>
protected override DataSourceView GetView(string viewName)
{
return new NUnitTestDataSourceView(this, viewName, this.AssemblyName);
}
}
/// <summary>
/// This is a NUnit test runner, that will find all test methods in an assembly and run them
/// </summary>
public class NUnitTestDataSourceView : DataSourceView
{
/// <summary>
/// This is the assembly that contains the unit tests
/// </summary>
private Assembly assembly;
/// <summary>
/// Initializes a new instance of the <see cref="NUnitTestDataSourceView"/> class.
/// </summary>
/// <param name="owner">The datasource owner</param>
/// <param name="viewName">Name of the view.</param>
/// <param name="assemblyName">Name of the assembly where the tests are located</param>
public NUnitTestDataSourceView(IDataSource owner, string viewName, string assemblyName)
: base(owner, viewName)
{
this.assembly = Assembly.Load(assemblyName);
}
/// <summary>
/// Gets a list of data from the underlying data storage.
/// </summary>
/// <param name="arguments">A <see cref="T:System.Web.UI.DataSourceSelectArguments"/> that is used to request operations on the data beyond basic data retrieval.</param>
/// <returns>
/// An <see cref="T:System.Collections.IEnumerable"/> list of data from the underlying data storage.
/// </returns>
protected override IEnumerable ExecuteSelect(DataSourceSelectArguments arguments)
{
List<UnitTestResult> result = new List<UnitTestResult>();
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;
}
/// <summary>
/// Determines whether the specified member has attribute.
/// </summary>
/// <param name="member">The member.</param>
/// <param name="attributeType">Type of the attribute.</param>
/// <returns>
/// <c>true</c> if the specified member has attribute; otherwise, <c>false</c>.
/// </returns>
private bool HasAttribute(MemberInfo member, Type attributeType)
{
return member.GetCustomAttributes(attributeType, true).Length > 0;
}
/// <summary>
/// Gets all test fixtures in the assembly.
/// </summary>
/// <returns>A list of types that are marked with the TestFixtureAttribute</returns>
private IEnumerable<Type> GetAllTestFixtures()
{
IList<Type> types = new List<Type>();
foreach (Type type in this.assembly.GetTypes())
{
if (this.HasAttribute(type, typeof(TestFixtureAttribute)))
{
types.Add(type);
}
}
return types;
}
/// <summary>
/// Gets all methods with the TestAttribute
/// </summary>
/// <param name="type">The type in where we will find the methods</param>
/// <returns>A list of MethodInfos that are marked with the TestAttribute</returns>
private IEnumerable<MethodInfo> GetAllMethods(Type type)
{
IList<MethodInfo> methods = new List<MethodInfo>();
foreach (MethodInfo method in type.GetMethods())
{
if (this.HasAttribute(method, typeof(TestAttribute)))
{
methods.Add(method);
}
}
return methods;
}
/// <summary>
/// Determines whether [is expected exception] [the specified method].
/// </summary>
/// <param name="method">The method that threw the exception</param>
/// <param name="e">The exception that was thrown</param>
/// <returns>
/// <c>true</c> if [is expected exception] [the specified method]; otherwise, <c>false</c>.
/// </returns>
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);
}
}
/// <summary>
/// The result of a UnitTest method
/// </summary>
public class UnitTestResult
{
/// <summary>
/// Initializes a new instance of the <see cref="UnitTestResult"/> class. The test run was success.
/// </summary>
/// <param name="name">The name of the method</param>
public UnitTestResult(string name)
: this(name, null)
{
this.Success = true;
}
/// <summary>
/// Initializes a new instance of the <see cref="UnitTestResult"/> class. The test run was a failure.
/// </summary>
/// <param name="name">The name of the test method</param>
/// <param name="exception">The exception that was thrown from the test</param>
public UnitTestResult(string name, Exception exception)
{
this.Name = name;
this.Exception = exception;
}
/// <summary>
/// Gets the name of the test method.
/// </summary>
/// <value>The name of the test method</value>
public string Name { get; private set; }
/// <summary>
/// Gets a value indicating whether this <see cref="UnitTestResult"/> is success or not.
/// </summary>
/// <value><c>true</c> if success; otherwise, <c>false</c>.</value>
public bool Success { get; private set; }
/// <summary>
/// Gets the exception thrown from the test method
/// </summary>
/// <value>The exception.</value>
public Exception Exception { get; private set; }
/// <summary>
/// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
/// </summary>
/// <returns>
/// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
/// </returns>
public override string ToString()
{
return this.Name;
}
}
}
<system.web>
<pages>
<controls>
<add tagPrefix="litemedia" namespace="LiteMedia.Utils.Web" assembly="LiteMedia.Utils" />
</controls>
</pages>
</system.web>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment