Ste*_*tes 5 .net c# unit-testing design-patterns
在很多TDD教程中,我看到这样的代码:
public class MyClass
{
public void DoSomething(string Data)
{
if (String.IsNullOrWhiteSpace(Data))
throw new NullReferenceException();
}
}
[Test]
public DoSomething_NullParameter_ThrowsException()
{
var logic = MyClass();
Assert.Throws<NullReferenceException>(() => logic.DoSomething(null));
}
Run Code Online (Sandbox Code Playgroud)
这一切都有意义,但在某些时候你将会到达实际使用MyClass的类,并且你想测试异常是否被处理:
public class EntryPointClass
{
public void DoIt(string Data)
{
var logicClass = new MyClass();
try
{
logicClass.DoSomething(Data);
}
catch(NullReferenceException ex)
{
}
}
}
[Test]
public DoIt_NullParameter_IsHandled()
{
var logic = new EntryPointClass()
try
{
logic.DoIt(null);
}
catch
{
Assert.Fail();
}
}
Run Code Online (Sandbox Code Playgroud)
那么为什么不把my/class中的try/catch放在首先,而不是抛出异常,并在MyClass单元测试类中测试为null,而不是在EntryPointClass单元测试类中?
通常,您的异常处理将如下所示:
public class EntryPointClass
{
Logger _logfile;
// ...
public void DoIt(string Data)
{
var logicClass = new MyClass();
try
{
logicClass.DoSomething(Data);
}
catch(NullReferenceException ex)
{
_logfile.WriteLine("null reference exception occured in method 'DoIt'");
}
}
}
Run Code Online (Sandbox Code Playgroud)
(这Logger只是一个示例,说明您需要进行正确的异常处理,但在您所在的地方不可用MyClass。您还可以在此处添加消息框的显示或类似的内容。)
在 级别MyClass,您通常没有适当的工具可用于正确的异常处理(并且您不想将它们添加到那里以保持类与特定日志记录机制等分离)。
请注意,此设计决策与 TDD 无关。如果您希望您的类MyClass实际上自己捕获异常,则必须以不同的方式编写测试,这是正确的。但是,只有当捕获异常不会阻止您的自动测试时,这才是一个好主意。例如,尝试为何MyClass时MyClass单独显示硬编码警告对话框编写一个良好的单元测试。
当然,上面的示例表明,EntryPointClass当您首先需要像记录器这样的东西来使事情正常工作时,单元测试实际上可能会变得更加困难。一般来说,您可以通过在构造时使用 ILogger 接口提供记录器来解决此问题,该接口允许用模拟替换记录器。或者在这个简单的情况下,根本不需要为单元测试初始化记录器并像这样编写代码:
public class EntryPointClass
{
// ....
catch(NullReferenceException ex)
{
if(_logfile!=null)
_logfile.WriteLine("null reference exception occured in method 'DoIt'");
}
// ....
}
Run Code Online (Sandbox Code Playgroud)