2017-10-17 96 views
1

考虑用try catch这样的方法:如何单元测试,如果调用静态记录发生

try 
{ 
    person = this.personRepo.GetPerson(name); 
} 
catch (PersonException) 
{ 
    LogHelper.LogDebug("PersonService", "No Person found!"); 
} 

在单元测试中,personRepo是伪造与FakeItEasy:

A.CallTo(() => this.personRepository.GetPerson(personName)).Throws(new PersonException("Person not found")); 

问题:

如何检查静态记录器是否被调用?

+0

因为'LogHelper.LogDebug'是静态方法 - 你不能嘲笑它。所以你需要测试'LogDebug'的实际行为。如果它写入文件 - 读取文件,如果写入数据库 - 读取数据库。如果'LogHelper'可配置 - 您可以将其配置为可以轻松测试的输出(例如MemoryStream)。但是洁具,它是静态方法,如果你并行运行测试 - 其他测试会受到相同配置的影响。适当的可测试解决方案 - 引入“记录器”的抽象并将其注入课堂。实际的实现仍然可以调用静态方法。 – Fabio

+0

这通常很复杂。这也是避免静力学的一个原因。您可以使用MS Fakes(Shim Types),但AFAIK您需要Visual Studio Enterprise(或以前的最终版本)。否则,您可能会应用重构。如果依赖注入不是您的选择,那么您可以首先引入一个单例LogHelper.Instance.LogDebug(...),它可以在单元测试中设置。 – Peit

+0

如果发生异常,您可以尝试测试。但在测试结束时,您应该删除已保存的数据。不知道这是否是最好的方法。 – Sasha

回答

4

这里的假设是personRepo被注入到被测主题中。因此我也假设你正在使用DI。

当前显示的代码与静态实现问题紧密耦合,这使得难以单独测试主题。

为了使主题更加灵活,更易于维护和隔离测试,主题需要重构以取决于抽象而不是结核。

在抽象背后封装静态LogHelper

public interface ILogger { 
    void LogDebug(string category, string msg); 
    //...other members 
} 

这将包装所需的行为/功能

public class LogHelperWrapper : ILogger { 
    public void LogDebug(string source, string msg) { 
     LogHelper.LogDebug(source, msg); 
    } 

    //...other members 
} 

主题会利用抽象,这将重构通过构造注射遵循显式依赖的原则。

private ILogger log; //set via constructor injection 
try { 
    person = this.personRepo.GetPerson(name); 
} catch (PersonException) { 
    this.log.LogDebug("PersonService", "No Person found!"); 
} 

这将允许被测试者在隔离测试时接收替换物。

//Arrange 
var logger = A.Fake<ILogger>(); 
//...other arrangements 

//Act 
//...exercise test 

//Assertion 
A.CallTo(() => logger.LogDebug("PersonService", "No Person found!")) 
.MustHaveHappened(Repeated.Exactly.Once); 
//...other assertions.