关于c#:如何正确进行单元测试

How to do unit tests correctly

我正在尝试使用测试驱动设计方法编写应用程序 - 我对单元测试很新,所以我只是想知道测试正确输入和异常的正确方法是什么。

我有这个用于加载配置文件的类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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种测试方法,如下所示?:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
        /// <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(){}语句吗?
    与第一次测试一样,隐含了正确性,在第2次和第3次测试中,我正在检查异常,因此异常对测试没有影响。


    你走的是正确的道路,但尚未到达。

    很少有你需要调用Assert.Inconclusive的情况,并且在你的情况下没有必要:当你期望异常并抛出异常时,它将按预期工作(也就是说:它应该显示出来)作为绿色结果)。当您期望异常并且没有抛出异常时,它将显示为失败(又名:红色结果)。更多关于这一点。

    事实上,可以测试返回void的方法。而不是返回值,它可能会改变某些事物的状态。在你的情况下变量configFile。测试方法的方法是检索值(例如通过提供一个getter)和/或使用依赖注入,并在测试中用变量替换假/模拟/存根(选择你的行话)。

    应该没有try-catch块:它只会隐藏代码可能存在的任何问题。至于你的原始代码:不要捕获实际的异常并将其重新抛出为FileNotFoundException。看看你隐藏的所有可能原因。

    扩展评论:

    I wouldn't want a develper to mess around with the configfile property directly, so should I make it public for the test, then change it back to private?

    这是一个很好的关注,并且每个开发人员在进行测试时都会面对它。重要的是要认识到单位的内部运作不是你应该经常测试的。实现细节正是它们的实现细节。然而,有时替代品甚至更不需要,所以你必须进行比较,无论你是否想要这样做。

    这可能是一个合法的用例,幸运的是有一个相当不错的解决方法!我在这里详细介绍,但我建议使用内部构造函数,方法,属性或字段来internal访问configFile。通过应用[InternalsVisibleTo]属性,您可以从单元测试项目中提供对它的访问,同时仍然将其隐藏在公共区域中。

    with regards to using stubs to test what's in configfile, it looks like I have to change everything to be interface dependant? Doesn't this just add tons more complexity that isn't needed?

    定义"需要什么"。确实,通过定义接口并注入这些接口并在代码中有一个额外的抽象层,但这样做是有原因的:由于类的松散耦合性,您现在可以通过注入另一个实现来更轻松地测试它。

    这个过程 - 依赖注入 - 是单元测试的支柱,也将有助于您的第一句话。