单元测试和编码设计

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单元测试类中?

Doc*_*own 3

通常,您的异常处理将如下所示:

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实际上自己捕获异常,则必须以不同的方式编写测试,这是正确的。但是,只有当捕获异常不会阻止您的自动测试时,这才是一个好主意。例如,尝试为何MyClassMyClass单独显示硬编码警告对话框编写一个良好的单元测试。

当然,上面的示例表明,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)