简体   繁体   English

如何在C#中使用Engine扩展构建自定义NUnit 3.x测试运行器

[英]How to build custom NUnit 3.x test runner with Engine extension in C#

I need to run a test N times where the number N is supplied through an external file. 我需要运行N次测试,其中N是通过外部文件提供的。 So N is not compile time constant. 因此N不是编译时间常数。 How can I achieve this? 我该如何实现? I have tried implementing ITestAction and IFrameworkDriver like the following. 我曾尝试实现ITestAction和IFrameworkDriver,如下所示。 When run a test in debug mode, the BeforeTest method is executed before the test runs and AfterTest after test is executed. 在调试模式下运行测试时,在测试运行之前执行BeforeTest方法,在测试执行之后执行AfterTest。 I assumed Run method would be executed just to run the test, but it doesn't. 我以为可以运行Run方法只是为了运行测试,但事实并非如此。 Your help is appreciated. 感谢您的帮助。

[Extension]
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class CustomRunner : Attribute, ITestAction, IFrameworkDriver
{

    private NUnit3FrameworkDriver frameworkDriver = new NUnit3FrameworkDriver(AppDomain.CurrentDomain);


    #region ITestAction
    public ActionTargets Targets => ActionTargets.Test;

    public bool IsTestRunning => throw new NotImplementedException();

    public void AfterTest(ITest test)
    {
        Console.WriteLine("After test is executed");
    }

    public void BeforeTest(ITest test)
    {
        Console.WriteLine("Before test is executed");
    }

    #endregion


    public string Load(string testAssemblyPath, IDictionary<string, object> settings)
    {
        Console.WriteLine("Testting");
        return frameworkDriver.Load(testAssemblyPath, settings);
    }

    public int CountTestCases(string filter)
    {
        Console.WriteLine("Testting");
        return frameworkDriver.CountTestCases(filter);
    }

    public string Run(ITestEventListener listener, string filter)
    {
        Console.WriteLine("Testting");
        return frameworkDriver.Run(listener, filter);
    }

    public string Explore(string filter)
    {
        Console.WriteLine("Testting");
        return frameworkDriver.Explore(filter);
    }

    public void StopRun(bool force)
    {
        Console.WriteLine("Testting");
        frameworkDriver.StopRun(force);
    }

    public string ID { get { return frameworkDriver.ID; } set { frameworkDriver.ID = value; } }
}

Test.cs Test.cs

[TestFixture]
public class Test 
{
    [CustomRunner]
    [Test]
    public void JustATest()
    {
        Assert.True("a".Equals("a"));
    }
}

NUnit 3 is based on a layered architecture: runners-engine-framework. NUnit 3基于分层架构:runners-engine-framework。 As in any such architecture, the layers only communicate with one another through defined APIs and their classes are never mixed. 就像在任何这样的体系结构中一样,这些层仅通过定义的API相互通信,并且它们的类从不混合。

Engine extensions are part of the engine layer, which is capable of loading and running tests using any framework for which a driver exists. 引擎扩展是引擎层的一部分,它可以使用存在驱动程序的任何框架来加载和运行测试。 Specifically, a framework driver extension is written to connect the engine to a framework that it doesn't understand natively. 具体而言,编写了框架驱动程序扩展,以将引擎连接到它本机无法理解的框架。 Drivers are extensions to the engine and are not part of the framework they support. 驱动程序是引擎的扩展,不是它们支持的框架的一部分。 So your use of the IFrameworkDriver interface is wrong both in it's purpose (you don't have a new framework to connect and there's already an NUnit 3 framework driver) and in it's implementation (having an attribute reference the engine and provide a driver has no effect, since nothing will ever call it). 因此,您使用IFrameworkDriver接口在目的(您没有要连接的新框架并且已经有NUnit 3框架驱动程序)和实现(具有属性引用引擎并提供驱动程序的属性)方面都是错误的效果,因为什么都不会调用它)。

The ITestAction interface is a part of the framework and your action will, if used correctly, be called before and after each test is run. ITestAction接口框架的一部分,如果操作正确,将在每次测试运行之前和之后调用您的操作。 However, it has no impact on how the test is run. 然而,这对测试是如何运行没有影响。

Since you want an attribute that works like the existing RetryAttribute but takes it's count from a file, we might consider deriving the new class from RetryAttribute . 由于您想要一个像现有RetryAttribute一样工作但从文件中获取计数的属性,因此我们可以考虑从RetryAttribute派生新类。 Unfortunately, although not sealed, the class uses private members and has no virtual methods to override. 不幸的是,尽管没有密封,但该类使用私有成员,并且没有虚拟方法可以覆盖。 It was not designed to be used as a base class. 它并非旨在用作基类。

OTOH, RetryAttribute is a very small class. OTOH, RetryAttribute是一个非常小的类。 My ultimate suggestion is that you simply copy and modify it. 我的最终建议是您只需复制并修改它。 Looking closely at its implementation will clarify a lot of things about how custom attributes work for you. 仔细研究其实现将阐明许多有关自定义属性如何为您工作的信息。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM