在我的业务逻辑中未设置配置密钥时,我已经提供了以下情况:
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正在吞噬异常以进行日志记录。测试通过没有try-catch。这是将AppSettings["SomeKey"]
手动设置为空字符串。
其次,如何在测试中指定someKey
中的DoStuff
为空以实际测试此情况而无需手动删除密钥设置?
我非常感谢任何帮助,因为我是单位测试的新手。
答案 0 :(得分:0)
首先,您的测试在设计上无效,因为您的方法实际上抛出调用代码的异常。这是因为您立即捕获并处理该异常。这实际上是异常的非常不正确的使用。没有必要根据条件投掷,然后在逻辑上需要做的就是检查那个条件时立即捕获。像这样:
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
属性的有效值或空字符串,具体取决于任何给定的测试需要。然后,您将验证条件语句产生的任何副作用。 (&#34;友好的错误消息&#34;以及&#34;记录&#34;我假设。这可能涉及进一步的嘲笑,我现在不能知道。)
当然,为了达到100%覆盖率,当外部设置时,您还需要为包装器的默认情况添加另一个测试。这应该是一个相当简单的测试:
// arrange
// no mocks to set up
// act
var presenter = new Presenter(null);
// assert
Assert.IsNotNull(presenter.Configuration);