我已经满足了在业务逻辑中没有设置配置键的情况,如下所示:
public class Presenter
{
private readonly IView view;
public Presenter(IView view)
{
this.view = view;
}
public void DoStuff()
{
try
{
String someKey = ConfigurationManager.AppSettings["SomeKey"].ToString();
if (string.IsNullOrEmpty(someKey))
{
throw new InvalidOperationException("SomeKey not set.");
}
// do stuff
}
catch (InvalidOperationException ex)
{
// provide view with friendly error
// log error
}
}
}
我试着测试这个错误是否发生在没有设置键的情况下:
[TestMethod]
public void Presenter_DoStuff_Should_Throw_InvalidOperationException_When_SomeKey_Not_Supplied()
{
// Arrange
mockIView = new Mock<IView>();
presenter = new Presenter(mockIView.Object);
// Act
// Assert
// NUnit here as more precise
NUnit.Framework.Assert.Throws<InvalidOperationException>(() => presenter.DoStuff(), "SomeKey not set.");
}
我如何让我的测试通过?它目前失败是因为try-catch为了记录日志而吞食了异常。没有试接,测试通过了。这是与
AppSettings["SomeKey"]
手动设置为空字符串。其次,我如何在测试中指定
DoStuff
中的someKey
为空以实际测试这种情况而无需手动删除键设置?
任何帮助是非常感谢,因为我是新的单元测试。
首先,您的测试在设计上是无效的,因为您的方法实际上并没有向调用代码抛出异常。这是因为您可以立即捕获并处理该异常。这实际上是异常的一种非常不正确的用法。没有必要根据条件抛出,然后在逻辑上需要做的只是检查该条件时立即捕获。像这样:
public void DoStuff()
{
var someKey = ConfigurationManager.AppSettings["SomeKey"];
if (string.IsNullOrEmpty(someKey))
{
// provide view with friendly error
// log error
return;
}
// do stuff
}
现在问题变成了……你在测试什么?此方法的实际业务逻辑在:
// do stuff
所以希望这是测试的关键焦点。现在,为了达到100%的代码覆盖率,您还需要测试那个条件块中的内容。为了做到这一点,你需要模拟环境。但是,您有一个外部依赖项:
ConfigurationManager
为了测试逻辑,您需要模拟该依赖项。一般的方法是为依赖项创建一种包装器对象。在本例中,它可以像
这样简单:public class ConfigurationWrapper
{
public virtual string SomeKey
{
get
{
return ConfigurationManager.AppSettings["SomeKey"];
}
}
}
这可以与具有DoStuff
的类分开,甚至可以嵌套在其中。这取决于你想在哪里/如何使用它。当然,它可以扩展到包装其他配置依赖项。然后在具有DoStuff
的类中为这个包装器创建一个属性。像这样:
private ConfigurationWrapper _config;
public ConfigurationWrapper Configuration
{
get
{
if (_config == null)
_config = new ConfigurationWrapper();
return _config;
}
set { _config = value; }
}
在DoStuff()
:
var someKey = this.Configuration.SomeKey;
现在对ConfigurationManager
的依赖被包装在一个可模拟对象中。因此,在单元测试中,您将创建一个模拟ConfigurationWrapper
对象,并将其设置在被测试的对象上。比如:
var mockConfig = new Mock<ConfigurationWrapper>();
presenter.Configuration = mockConfig;
可以将mock设置为返回.SomeKey
属性的有效值或空字符串,这取决于任何给定的测试需要什么。然后验证条件语句产生的副作用。(我认为是"友好的错误消息"one_answers"日志记录"。这可能涉及到进一步的模拟,我现在还不知道。
当然,要达到100%的覆盖率,您还需要在没有外部设置包装器的默认情况下添加另一个测试。这应该是一个相当简单的测试:
// arrange
// no mocks to set up
// act
var presenter = new Presenter(null);
// assert
Assert.IsNotNull(presenter.Configuration);