对其中包含 Environment.Exit() 调用的方法进行单元测试

Unit Testing a method containing Environment.Exit() call within it

我有一个 public 方法说,

public void ErrorEncounter()
{
//Global Error Counter
gblErrorCount++;

//Process tremination
Environment.Exit();
}

此方法在调用时终止。但是,它将更新我想测试的 全局错误计数 。有什么方法可以对这个方法进行单元测试吗?

我正在使用 NUnit 单元测试框架。

这个方法被设计成很难测试!

最明显,因为它会在调用时终止应用程序。但也因为它改变了一个全局(我假设是静态的)变量。这两件事都会阻止编写调用该方法的良好单元测试。

三种解决方法: 1.消除方法 2.不测试方法 3.修改方法

方案一,如果这个方法只调用了exit,那么你可以直接放弃,直接调用exit。但是,这会使其他一些方法难以测试,因此这并不是一个很好的选择。

选项2。有时候一个方法很简单,你可以避免测试它。这是否是这样的方法取决于gblErrorCount在其他地方如何使用。但是,增加计数似乎没有任何效果,因为进程会立即退出。

方案3.修改方法和调用它的那些方法。一种方法是使用事件处理机制并在事件处理程序中终止应用程序。您可以通过在 运行 测试时注入不同的事件处理程序来简化测试。

IOW,这基本上是一种无法测试的方法。希望您可以控制被测系统并可以对其进行更改。

包含显示如何测试 Environment.Exit() 的答案。 构造函数依赖注入

一种选择是通过接口注入将其转换为依赖项:

interface ITerminator
{
    void Exit();
}

class RealTerminator
{
    void Exit()=>Environment.Exit();
}

public class MyErrorChecker
{
    ITerminator _terminator;
    public class MyErrorChecker(ITerminator terminator)
    {
        _terminator=terminator;
    }

    public void ErrorEncounter()
    {
        //Global Error Counter
        gblErrorCount++;

        //Process tremination
        _terminator.Exit();
    }
}

测试项目将实现一个伪终止符 class,如果调用 Exit,它会设置一个标志:

class FakeTerminator:ITerminator
{
    public bool Called{get;private set;}
    public void Exit()
    {
        Called=true;
    }
}

嘲讽

另一种选择是通过提取对可以在模拟中替换的虚拟方法的调用来模拟它 class :

public void ErrorEncounter()
{
    //Global Error Counter
    gblErrorCount++;

    //Process tremination
    ForceExit();
}

internal virtual void ForceExit()
{
    Environment.Exit();
}

测试项目可以创建一个模拟错误检查器class:

class MockErrorChecker:MyErrorChecker
{
    public bool Called{get;private set;}
    public override void ForceExit()
    {
        Called=true;
    }
}

函数注入

此选项未包含在链接的问题中。将 exit Action 作为参数传递给 ErrorEncounter,其默认调用 Environment.Exit() :

    public void ErrorEncounter(Action exitFn=null)
    {
        var doExit=exitFn ?? (()=>Environment.Exit());
        //Global Error Counter
        gblErrorCount++;

        //Process tremination
        doExit();
    }

测试可以通过它自己的设置标志的函数:

[Test]
public void Test_Exit_Is_Called
{
    bool called;
    void fakeExit() { called=true; }

    thatClass.ErrorEncounter(fakeExit);

    Assert.True(called);
}