Reputation: 62804
I would like to be able to simulate a failure of an arbitrary test in order to check that my TearDown logic works correctly. A unit test on a unit test, if you please.
But really, I have TearDown in some fixtures that produces attachments on failures. I must be able to show off this feature, but it is hard to produce a failure when you most need it.
So, I want to create a test parameter specifying the name of a test I wish to fail. Now, I can easily write an attribute implementing IWrapTestMethod
or IApplyToContext
, but then I need to apply it on every test method.
Is there a way to implement it without touching every test and/or fixture? By some kind of an assembly level attribute or assembly level setup method that would run before each and every test?
It is crucial that this logic would not prevent the TearDown
methods from running, so ITestAction
throwing an exception from BeforeTest
does not fit the bill.
Can this be done?
Upvotes: 1
Views: 129
Reputation: 62804
I found the solution:
using NUnit.Framework;
using NUnit.Framework.Interfaces;
using NUnit.Framework.Internal;
using System;
using System.Reflection;
[assembly: Common.EnableFailureSimulation]
namespace Common
{
public class SimulateFailureMethodInfoWrapper : IMethodInfo
{
private readonly IMethodInfo m_mi;
public SimulateFailureMethodInfoWrapper(IMethodInfo mi)
{
m_mi = mi;
}
public ITypeInfo TypeInfo => m_mi.TypeInfo;
public MethodInfo MethodInfo => m_mi.MethodInfo;
public string Name => m_mi.Name;
public bool IsAbstract => m_mi.IsAbstract;
public bool IsPublic => m_mi.IsPublic;
public bool ContainsGenericParameters => m_mi.ContainsGenericParameters;
public bool IsGenericMethod => m_mi.IsGenericMethod;
public bool IsGenericMethodDefinition => m_mi.IsGenericMethodDefinition;
public ITypeInfo ReturnType => m_mi.ReturnType;
public T[] GetCustomAttributes<T>(bool inherit) where T : class => m_mi.GetCustomAttributes<T>(inherit);
public Type[] GetGenericArguments() => m_mi.GetGenericArguments();
public IParameterInfo[] GetParameters() => m_mi.GetParameters();
public object Invoke(object fixture, params object[] args)
{
var res = m_mi.Invoke(fixture, args);
Assert.Fail("Failure simulation");
return res;
}
public bool IsDefined<T>(bool inherit) where T : class => m_mi.IsDefined<T>(inherit);
public IMethodInfo MakeGenericMethod(params Type[] typeArguments) => m_mi.MakeGenericMethod(typeArguments);
}
[AttributeUsage(AttributeTargets.Assembly)]
public class EnableFailureSimulationAttribute : Attribute, ITestAction
{
private static string s_failTestMethod = GetParameterByName("!");
public ActionTargets Targets => ActionTargets.Test;
public void AfterTest(ITest test)
{
}
public void BeforeTest(ITest test)
{
if (test.MethodName == s_failTestMethod && test is Test testImpl)
{
testImpl.Method = new SimulateFailureMethodInfoWrapper(testImpl.Method);
s_failTestMethod = "!";
}
}
}
}
An alternative approach would be to use Moq
and mock the IMethodInfo
interface instead of having the real SimulateFailureMethodInfoWrapper
class.
Anyway, this seems to work perfectly.
Upvotes: 1