我有一个公开方法,
public void ErrorEncounter()
{
//Global Error Counter
gblErrorCount++;
//Process tremination
Environment.Exit();
}
此方法在调用时终止。但是,它会更新我想测试的全局错误计数。有什么方法可以对此方法执行单元测试吗?
我正在使用 NUnit 进行单元测试的框架。
答案 0 :(得分:4)
这种方法被设计为难以测试!
最明显的是,因为它在被调用时会终止应用程序。也是因为它更改了全局变量(我假设是静态的)。这两件事都阻止编写好的方法调用单元测试。
三种解决方法: 1.消除方法 2.不要测试方法 3.修改方法
选项1.如果此方法仅调用了exit,则可以直接将其删除并直接调用Exit。但是,这会使其他方法难以测试,因此这并不是一个好选择。
选项2。有时,方法是如此简单,以至于您可以避免对其进行测试。是否是这样的方法取决于gblErrorCount
在其他地方如何使用。但是,由于该过程立即退出,因此增加计数似乎没有效果。
选项3.修改方法和调用该方法的那些方法。一种方法是使用事件处理机制,并在事件处理程序中终止应用程序。您可以通过在运行测试时注入不同的事件处理程序来使其更易于测试。
IOW,这基本上是一种无法测试的方法。希望您可以控制被测系统,并且可以对其进行更改。
答案 1 :(得分:0)
This question包含的答案显示了如何测试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();
}
}
如果调用Exit
,则测试项目将实现伪造的终结器类,该类将设置标志:
class FakeTerminator:ITerminator
{
public bool Called{get;private set;}
public void Exit()
{
Called=true;
}
}
模拟
另一个选择是通过提取对虚拟方法的调用来模拟它,该方法可以在模拟类中替换:
public void ErrorEncounter()
{
//Global Error Counter
gblErrorCount++;
//Process tremination
ForceExit();
}
internal virtual void ForceExit()
{
Environment.Exit();
}
测试项目可以创建一个模拟错误检查器类:
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);
}