C# 静态方法调用是单元测试的测试用例吗?

C# 静态方法调用是单元测试的测试用例吗?,c#,unit-testing,moq,C#,Unit Testing,Moq,这是我们班的简化模型 public static FooFactory { public void CreateFooByUrl(string url) { try { // business logic } catch(Exception exc) { ApplicationLogger.LogError(exc); } }

这是我们班的简化模型

public static FooFactory
{
    public void CreateFooByUrl(string url) 
    {
        try 
        {
           // business logic
        }
        catch(Exception exc) 
        {
           ApplicationLogger.LogError(exc);
        }
    }
}
ApplicationLogger
是一个在整个解决方案中使用的静态类。如何验证已记录错误

这是我的测试方法的例子

[TestMethod]
public void CreateFooExpectedError()
{
    // Arrange
    string testUrl = "fakeUrl";

    // Act
    FooFactory.CreateFoo(testUrl);

    // Assert
    /ApplicationLogger.LogError();
}

如何检查调用了
LogError
方法?它是一个测试用例吗

您对
ApplicationLogger
有一个很强的依赖性。那不好。现在,如果不实际记录一些内容,就无法测试
CreateFooByUrl
是否有效。相反,让它使用
iaapplicationlogger
(接口),然后您可以提供该接口的模拟实现,作为单元测试的一部分。这可能意味着您需要将
FooFactory
设置为非静态(您不能像前面所示那样在静态类中使用非静态方法),或者更改
CreateFooByUrl
以接受
iaapplicationLogger
作为参数(messier)

这是一个干净的方法:

public interface IApplicationLogger
{
    void LogError(Exception exception);
}

public class ApplicationLogger : IApplicationLogger
{
    public void LogError(Exception exception)
    {
        //do stuff
    }
}

public class FooFactory
{
    private readonly IApplicationLogger _logger;

    public FooFactory(IApplicationLogger logger)
    {
        _logger = logger;
    }

    public void CreateFooByUrl(string url) 
    {
        try 
        {
           // business logic
        }
        catch(Exception exception) 
        {
           _logger.LogError(exception);
        }
    }
}

//now for the unit test
public void TestCreate()
{
    //arrange
    var mockLogger = new Mock<IApplicationLogger>(MockBehavior.Strict);
    mockLogger.Setup(m => m.LogError(It.IsAny<Exception>()));

    var factory = new FooFactory(mockLogger.Object);

    //act
    factory.CreateFooByUrl("something that will cause exception");


    //assert
    mockLogger.Verify(m => m.LogError(It.IsAny<Exception>()));
}
公共接口IApplicationLogger
{
无效日志错误(异常);
}
公共类ApplicationLogger:IAApplicationLogger
{
公共无效日志错误(异常)
{
//做事
}
}
公营食品厂
{
专用只读IApplicationLogger\u记录器;
公共食品工厂(IApplicationLogger记录器)
{
_记录器=记录器;
}
公共void CreateFooByUrl(字符串url)
{
尝试
{
//业务逻辑
}
捕获(异常)
{
_logger.LogError(异常);
}
}
}
//现在进行单元测试
公共void TestCreate()
{
//安排
var mockLogger=new Mock(MockBehavior.Strict);
mockLogger.Setup(m=>m.LogError(It.IsAny());
var工厂=新的FooFactory(mockLogger.Object);
//表演
CreateFooByUrl(“将导致异常的内容”);
//断言
验证(m=>m.LogError(It.IsAny());
}

从关注点分离的角度来看,这要好得多。为什么FooFactory应该关心事情是如何被记录的?它只需要能够记录事情。这就是为什么最好针对接口编写代码。然后,如果您想要切换日志实现,您只需创建一个实现IApplicationLogger的新类,一切都会神奇地工作。

您对
ApplicationLogger
有很强的依赖性。那不好。现在,如果不实际记录一些内容,就无法测试
CreateFooByUrl
是否有效。相反,让它使用
iaapplicationlogger
(接口),然后您可以提供该接口的模拟实现,作为单元测试的一部分。这可能意味着您需要将
FooFactory
设置为非静态(您不能像前面所示那样在静态类中使用非静态方法),或者更改
CreateFooByUrl
以接受
iaapplicationLogger
作为参数(messier)

这是一个干净的方法:

public interface IApplicationLogger
{
    void LogError(Exception exception);
}

public class ApplicationLogger : IApplicationLogger
{
    public void LogError(Exception exception)
    {
        //do stuff
    }
}

public class FooFactory
{
    private readonly IApplicationLogger _logger;

    public FooFactory(IApplicationLogger logger)
    {
        _logger = logger;
    }

    public void CreateFooByUrl(string url) 
    {
        try 
        {
           // business logic
        }
        catch(Exception exception) 
        {
           _logger.LogError(exception);
        }
    }
}

//now for the unit test
public void TestCreate()
{
    //arrange
    var mockLogger = new Mock<IApplicationLogger>(MockBehavior.Strict);
    mockLogger.Setup(m => m.LogError(It.IsAny<Exception>()));

    var factory = new FooFactory(mockLogger.Object);

    //act
    factory.CreateFooByUrl("something that will cause exception");


    //assert
    mockLogger.Verify(m => m.LogError(It.IsAny<Exception>()));
}
公共接口IApplicationLogger
{
无效日志错误(异常);
}
公共类ApplicationLogger:IAApplicationLogger
{
公共无效日志错误(异常)
{
//做事
}
}
公营食品厂
{
专用只读IApplicationLogger\u记录器;
公共食品工厂(IApplicationLogger记录器)
{
_记录器=记录器;
}
公共void CreateFooByUrl(字符串url)
{
尝试
{
//业务逻辑
}
捕获(异常)
{
_logger.LogError(异常);
}
}
}
//现在进行单元测试
公共void TestCreate()
{
//安排
var mockLogger=new Mock(MockBehavior.Strict);
mockLogger.Setup(m=>m.LogError(It.IsAny());
var工厂=新的FooFactory(mockLogger.Object);
//表演
CreateFooByUrl(“将导致异常的内容”);
//断言
验证(m=>m.LogError(It.IsAny());
}

从关注点分离的角度来看,这要好得多。为什么FooFactory应该关心事情是如何被记录的?它只需要能够记录事情。这就是为什么最好针对接口编写代码。然后,如果您想要切换日志实现,您只需创建一个实现IApplicationLogger的新类,一切都会神奇地工作。

@mason但是在其中包含try-catch块有意义吗?一般来说,我对“一网打尽”的方法不太满意。这将是一个完全不同的问题,如果不了解更多有关应用程序的信息,我无法回答这个问题。
ApplicationLogger
通常是如何配置的?你能动态地添加日志目的地吗?@mason我同意,我认为这应该是OP的一部分,他的问题是为什么要抛出错误。我认为依赖错误日志记录的工作流比早期失败工作流危险得多。从这里的结构(之后没有抛出)来看,我宁愿看到在
CreateFooByUrl
抛出一个错误后出现空错误,而foo没有created@Icepickle诚然,应该允许异常进入任何可以处理它的地方,实际上不应该使用异常,而应该使用请求/响应模式。但是我没有把这作为我回答的一部分,因为我认为学习国际奥委会的基本知识是这里的重点。“虽然这是一个很好的观点,”梅森说,“但是在那里设置try-catch块有意义吗?”?一般来说,我对“一网打尽”的方法不太满意。这将是一个完全不同的问题,如果不了解更多有关应用程序的信息,我无法回答这个问题。
ApplicationLogger
通常是如何配置的?你能动态地添加日志目的地吗?@mason我同意,我认为这应该是OP的一部分,他的问题是为什么要抛出错误。我认为依赖错误日志记录的工作流比早期失败工作流危险得多。从这里的结构来看(之后没有抛出),我宁愿看到出现空错误