Background
I’m developing some NuGets that form an infrastructure for test automation based on NUnit that is used throught my organization.
As part of it I have a method (let’s call it OnTearDown
) which should only be called from a method with [TearDown]
attribute. In order to ensure that, OnTearDown
calls the following method:
private static void AssertInTearDown(string methodName)
{
var isInTearDown = IsMethodWithAttributeOnStack<TearDownAttribute>();
if (!isInTearDown)
throw new InvalidOperationException(
$"Method {methodName} should only be called from a method with a [TearDown] attribute");
}
private static bool IsMethodWithAttributeOnStack<TAttribute>()
where TAttribute : Attribute
{
var stack = new StackTrace();
return stack.GetFrames().Any(frame => frame.GetMethod()?.HasAttribute<TAttribute>() ?? false);
}
Some of my (older) clients call OnTearDown
directly (from their [TearDown]
decorated methods), but I also have a TestBase
class which has its own [TearDown]
method (also called TearDown
) that most of the newer clients use.
While our clients we were using .Net 6 everything worked fine.
The problem
Recently we started migrating both our client projects and our NuGets to .Net 8. Some projects passed the migration seamlessly, but few started encountering a very strange problem. When investigated, we saw that in these projects the problem occurs both with our older (.Net 6) packages and with the newer (.net 8) ones, as long as the referencing project is .Net 8.
In these projects, some of the tests fail on tear down with the following error:
TearDown : System.InvalidOperationException : Method OnTearDown should only be called from a method with a [TearDown] attribute
Stack Trace:
at AssertInTearDown()
at OnTearDown()
at InvokeStub_TestBase.TearDown(Object, Object, IntPtr*)
at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)
Note: I modified the real stack trace here for the sake of clarity.
The most interesting and suspicious thing here is the line InvokeStub_TestBase.TearDown(Object, Object, IntPtr*)
. My real class is called TestBase
and not InvokeStub_TestBase
, and it has no arguments (let along an IntPtr*
…). I believe that .Net re-creates my TearDown
method for some kind of optimization, and for some reason it doesn’t have the [TearDown]
attribute.
My questions
- If anyone can shed some light on what’s going on here (e.g. what’s that
InvokeStub
), that would be very helpful - Any idea on how can I fix it?