// // 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; } } }