我尝试使用测试驱动设计方法编写应用程序 - 我对单元测试很陌生,所以我只是想知道测试正确输入和异常的正确方法是什么是
我有这个用于加载配置文件的类:
class Config
{
private XmlDocument configfile;
public Config()
{
configfile = new XmlDocument();
}
public void LoadConfigFile(string filename)
{
if(string.IsNullOrEmpty(filename))
throw new System.ArgumentException("You must specify a filename");
try
{
configfile.Load(filename);
}
catch (Exception ex)
{
throw new System.IO.FileNotFoundException("File could not be loaded");
}
}
}
因此,可以在此处执行3项测试:
测试这些是正确的方法,编写3种测试方法,如此吗?:
/// <summary>
///A test for LoadConfigFile
///</summary>
[TestMethod()]
public void LoadConfigFileTest()
{
Config target = new Config(); // TODO: Initialize to an appropriate value
string filename = "config.xml"; // TODO: Initialize to an appropriate value
target.LoadConfigFile(filename);
Assert.Inconclusive("A method that does not return a value cannot be verified.");
}
/// <summary>
///A test for LoadConfigFile
///</summary>
[TestMethod()]
[ExpectedException(typeof(System.ArgumentException))]
public void LoadConfigFileTest1()
{
Config target = new Config(); // TODO: Initialize to an appropriate value
string filename = ""; // TODO: Initialize to an appropriate value
target.LoadConfigFile(filename);
Assert.Inconclusive("A method that does not return a value cannot be verified.");
}
/// <summary>
///A test for LoadConfigFile
///</summary>
[TestMethod()]
[ExpectedException(typeof(System.IO.FileNotFoundException))]
public void LoadConfigFileTest2()
{
Config target = new Config(); // TODO: Initialize to an appropriate value
string filename = "blah.xml"; // TODO: Initialize to an appropriate value
target.LoadConfigFile(filename);
Assert.Inconclusive("A method that does not return a value cannot be verified.");
}
另外,如果所有这三个测试都有try {} catch(){}语句吗? 与第一次测试一样,隐含了正确性,在第二次和第三次测试中,我无论如何都要检查异常,因此异常对测试没有影响。
答案 0 :(得分:4)
你走的是正确的道路,但尚未到达。
很少有情况需要调用Assert.Inconclusive
并且在您的情况下没有必要:当您预期异常并抛出异常时,它将按预期工作(又名:它应该显示为绿色结果)。当您期望异常并且没有抛出异常时,它将显示为失败(又名:红色结果)。更多关于here。
事实上,a method that returns void
can be tested。而不是返回值,它可能会改变某些事物的状态。在您的情况下变量configFile
。测试方法的方法是检索值(例如通过提供一个getter)和/或使用依赖注入,并在测试中用变量替换假/模拟/存根(选择你的行话)。
应该没有try-catch块:它只会隐藏代码可能存在的任何问题。至于原始代码:不要捕获实际异常并将其重新抛出为FileNotFoundException
。 Look at all the possible causes you are hiding
扩展评论:
我不希望开发人员直接使用configfile属性,所以我应该将其公开用于测试,然后将其更改回私有?
这是一个很好的关注,并且每个开发人员在进行测试时都会面对它。重要的是要认识到单位的内部运作不是你应该通常测试的东西。实现细节正是它们的实现细节。然而,有时替代品甚至更不需要,所以你必须进行比较,无论你是否想要这样做。
这可能是一个合法的用例,幸运的是有一个相当不错的解决方法!我在其中详细介绍here但我建议internal
configfile
访问[InternalsVisibleTo]
是否使用内部构造函数,方法,属性或仅使用字段。通过应用{{1}}属性,您可以从单元测试项目中访问它,同时仍然将其隐藏在公共场所。
关于使用存根来测试configfile中的内容,看起来我必须将所有内容更改为依赖于接口?这不仅会增加更多不需要的复杂性吗?
定义“需要什么”。确实,通过定义接口并注入这些接口并在代码中有一个额外的抽象层,但这样做是有原因的:由于类的松散耦合性,您现在可以通过注入另一个实现来更轻松地测试它。 / p>
此过程 - 依赖注入 - 是单元测试的支柱,也将有助于您的第一次评论。