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的一部分,他的问题是为什么要抛出错误。我认为依赖错误日志记录的工作流比早期失败工作流危险得多。从这里的结构来看(之后没有抛出),我宁愿看到出现空错误