如何在单元测试中获取运行时的单元测试方法名?

31
如何从单元测试中获取单元测试名称?
我在BaseTestFixture类中有以下方法:
public string GetCallerMethodName()
{
    var stackTrace = new StackTrace();
    StackFrame stackFrame = stackTrace.GetFrame(1);
    MethodBase methodBase = stackFrame.GetMethod();
    return methodBase.Name;
}

我的测试固件类继承自基类:

[TestFixture]
public class WhenRegisteringUser : BaseTestFixture
{
}

我有以下系统测试:

[Test]
public void ShouldRegisterThenVerifyEmailThenSignInSuccessfully_WithValidUsersAndSites()
{
    string testMethodName = this.GetCallerMethodName();
    //
}

在 Visual Studio 中运行此代码时,它会按预期返回我的测试方法名称。

但是当这个代码通过 TeamCity 运行时,返回的是_InvokeMethodFast(),这似乎是 TeamCity 在运行时为自己生成的一个方法。

那么,我该如何在运行时获取测试方法的名称呢?


从VS启动时是否以发布模式编译? - Adriano Repetti
1
你能获取堆栈中每个调用方法的程序集信息吗?也许你可以沿着堆栈向上查找你的测试程序集。 - George Duckett
一个堆栈跟踪会有所帮助,以查看实际方法名称是否在调用堆栈的更上层中列出。 - Joachim Isaksson
你有超过60个问题没有得到采纳。你觉得这样对帮助你的人尊重吗? - Rune FS
5个回答

26

如果您正在使用NUnit 2.5.7 / 2.6,则可以使用TestContext类

[Test]
public void ShouldRegisterThenVerifyEmailThenSignInSuccessfully()
{
    string testMethodName = TestContext.CurrentContext.Test.Name;
}

2
MSTest同样也在其TestContext中公开了这些信息。 - bryanbcook
现在它从TeamCity中运行良好,但是当我在Visual Studio内部本地运行它时,它会抛出一个未初始化的对象异常。那么如何使其在Visual Studio内部工作? - The Light
你使用的测试运行器是哪个? - nemesv
这个答案在2022年仍然适用于NUnit 3.13。 - Theophilus

23

如果在测试类中添加TestContext属性,使用Visual Studio运行测试时可以轻松获取此信息。

[TestClass]
public class MyTestClass
{
    public TestContext TestContext { get; set; }

    [TestInitialize]
    public void setup()
    {
        logger.Info(" SETUP " + TestContext.TestName);
        // .... //
    }
}

7

感谢大家,我采用了综合方法,现在它可以在所有环境下工作:

public string GetTestMethodName()
{
    try
    {
        // for when it runs via TeamCity
        return TestContext.CurrentContext.Test.Name;
    }
    catch
    {
        // for when it runs via Visual Studio locally
        var stackTrace = new StackTrace(); 
        foreach (var stackFrame in stackTrace.GetFrames())
        {
            MethodBase methodBase = stackFrame.GetMethod();
            Object[] attributes = methodBase.GetCustomAttributes(
                                      typeof(TestAttribute), false); 
            if (attributes.Length >= 1)
            {
                return methodBase.Name;
            }
        }
        return "Not called from a test method";  
    }
}

请注意,在发布版本中,有时这可能不起作用 - 测试方法可能会被内联,您将无法看到TestAttribute。 - imaximchuk

7

如果您没有使用NUnit,您可以循环遍历堆栈并找到测试方法:

foreach(var stackFrame in stackTrace.GetFrames()) {
  MethodBase methodBase = stackFrame.GetMethod();
  Object[] attributes = methodBase.GetCustomAttributes(typeof(TestAttribute), false);
  if (attributes.Length >= 1) {
    return methodBase.Name;
  } 
}
return "Not called from a test method";

你知道这个只能在MSTest上运行吗?还是它可以在NUnit和MSTest上都运行? - Yoiku
您需要更改注释,以符合测试框架的要求。然而正如被接受的回答所指出的那样,NUnit有一个TestContext,它是更好的解决方案。 - Michael Lloyd Lee mlk
谢谢你的回答,我问这个问题是因为我正在尝试构建一个FW,我不想限制自己只使用NUnit或MSTest。所以我想做的是从底部到顶部循环堆栈,直到找到一个命名空间不是Microsoft或System的方法。我已经尝试过这个方法,并且在MSTest中调用FW时(我得到了主方法)取得了良好的结果。 - Yoiku
请注意,在发布版本中,有时这可能不起作用 - 测试方法可能会被内联,您将无法看到TestAttribute。 - imaximchuk
这是一个 LINQ 一行代码:string testName = new StackTrace().GetFrames()?.Select(stackFrame => stackFrame.GetMethod()).FirstOrDefault(m => m.GetCustomAttributes(typeof(TestAttribute), false).Any())?.Name ?? "Not called from a test method"; - Alain

1
如果您没有使用Nunit或任何其他第三方工具,您将无法获得TestAttribute。 因此,您可以使用TestMethodAttribute而不是TestAttribute来获取测试方法名称。
    public string GetTestMethodName()
    {
            // for when it runs via Visual Studio locally
            var stackTrace = new StackTrace();
            foreach (var stackFrame in stackTrace.GetFrames())
            {
                MethodBase methodBase = stackFrame.GetMethod();
                Object[] attributes = methodBase.GetCustomAttributes(typeof(TestMethodAttribute), false);
                if (attributes.Length >= 1)
                {
                    return methodBase.Name;
                }
            }
            return "Not called from a test method";
    }

1
这是一个 LINQ 单行代码:public string GetTestMethodName() => new StackTrace().GetFrames()?.Select(stackFrame => stackFrame.GetMethod()).FirstOrDefault(m => m.GetCustomAttributes(typeof(TestMethodAttribute), false).Any())?.Name ?? "Not called from a test method"; - Alain

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接