【问题标题】:Is it important to unit test a constructor?对构造函数进行单元测试重要吗?
【发布时间】:2010-09-26 08:30:46
【问题描述】:

我应该对构造函数进行单元测试吗?假设我有一个这样的构造函数:

IMapinfoWrapper wrapper;
public SystemInfo(IMapinfoWrapper mapinfoWrapper)
{
    this.wrapper = mapinfoWrapper;
}

我需要为此构造器编写单元测试吗?我没有任何包装变量的 getter,所以我不需要测试它。

【问题讨论】:

    标签: c# .net unit-testing constructor


    【解决方案1】:

    没有。它的功能将由该类的所有其他单元测试进行测试。

    【讨论】:

    • 没错,我什至没有想到这一点。 拍脑门
    • 如果里面有多个构造函数或多个控制流,则不是每个。
    【解决方案2】:

    是的。如果您的构造函数中有逻辑,则应该对其进行测试。简单地设置属性不是逻辑 IMO。条件、控制流等 IS 逻辑。

    编辑: 如果需要该依赖项,您可能应该测试 IMapinfoWrapper 何时为空。如果是这样,那么这就是逻辑,你应该有一个测试来捕获你的 ArgumentNullException 或其他什么......你的测试是定义代码行为方式的规范。如果它抛出 ArgumentNullException,那么应该在测试中指定。

    【讨论】:

    • 我从来没有在我的构造函数中加入任何逻辑。只是二传手。
    • @BrianGenisio,感谢您的提示,我刚刚在我的应用程序中添加了一些 ArgumentNullException 测试和代码...出于某种原因,从未有过...
    【解决方案3】:

    单元测试是关于测试对象的公共状态、行为和交互。

    如果你只是在构造函数中设置一个私有字段,有什么要测试的?

    不要费心对简单的访问器和修改器进行单元测试。这很愚蠢,对任何人都没有帮助。

    【讨论】:

    • 如果您的构造函数有,例如,一个 if(条件),您需要测试两个流(真,假)。如果您的构造函数在设置之前做了某种工作。您应该检查工作是否已完成。
    • 如果在构造函数中有checkArgumnt 怎么办(可能是为了测试对象是否为空)?值得测试吗?
    • 是的,通常值得用自己的复杂行为测试构造函数。
    • 我知道我来晚了 - 但一般来说,我不认为构造函数是放置逻辑或验证的好地方。而是使用例如某种针对你的类的请求验证器操作对我来说可能更有意义。即使我们应该在构造函数中检查空引用,但我在单元测试中看不到这一点,为什么?因为如果该对象为空,您的操作应该会失败,这意味着“行为”失败,我们应该对其进行单元测试,而不是对实际的构造函数进行单元测试。
    【解决方案4】:

    我相信 100% 的覆盖率。此外,100% 的覆盖率不是通过简单地通过模拟事物或设置和获取事物来测试简单的交互,而是通过更多的集成/验收测试来检查功能。因此,如果您最终编写了非常好的集成/验收测试,则应该调用您的所有构造函数(以及简单的方法,例如 setter 和 getter)。

    【讨论】:

    • 100% 覆盖率是白日梦,任何告诉你不同的人都是在向你推销一个闪亮的新测试工具。 ;) 我开玩笑……有点。
    • 这可能是遗留系统的问题,但如果你从头开始一个新项目,我认为 100% 是唯一的方法。我曾在具有 100% 覆盖率的系统上工作过。我们试驾了一切。
    • 好的,你有 100% 的覆盖率。但是您是否测试了所有执行路径?第一个并不意味着第二个。
    • 真正的 100% 可以很容易地通过只做一些事情的测试来实现,并且在微观级别上进行测试。但是 100% 覆盖您认为是系统功能的测试。即使没有考虑到每个执行路径,路径也会让您对维护或新工作更有信心。
    • 100% coverage of that you believe to be the system's functional paths. 换句话说:不是 100%
    【解决方案5】:

    SystemInfo 实例的什么行为取决于wrapper 的值?

    如果出现任何问题(例如 null 值导致损坏),那么我建议编写描述每种此类情况的场景并使用它们来驱动适当单元测试的定义。

    如果所有场景最终都依赖于 IMapinfoWrapper 的私​​有实例的状态或行为,那么我建议改为在该类上编写单元测试。

    【讨论】:

      【解决方案6】:

      单元测试是关于检查执行路径,通常称为Cyclomatic Complexity

      如果您没有路径可供选择,没有 if、没有循环、没有 GOTO (=P),它就不是很有用

      【讨论】:

      • 我不同意你的观点,即在没有任何 ifs/loops/gotos 的情况下测试方法是没有意义的。总是有至少一个执行路径,即使它是唯一的,你也应该测试它。
      • Eric,如何测试边界案例——并测试边界之间的代表值。例如,输入 0 和 1 可能被视为平方根函数的边界情况,因为在那里输入值和输出值相等。所以测试,例如,-10、-1、-0.5、0、0.5、1 和 10。或者也测试整个范围。如果您的函数应该适用于高达 4096 的输入,请尝试 4095 和 4097...
      【解决方案7】:

      问:如果在构造函数中设置成员变量,为什么要设置呢?

      答:因为你有一个失败的单元测试,只有在构造函数中设置它才能通过。

      如果您使用这种逻辑,您只编写代码以使单元测试通过(测试驱动开发),那么您已经有了问题的答案。

      【讨论】:

        【解决方案8】:

        除非您正在编写编译器,否则不会,因为您只会测试编译器是否可以生成代码来执行分配,这通常是没有意义的。

        现在,在更常见的情况下,如果您想用包装器做其他事情,那么也许有一点。例如,如果您尝试传递 null,则可以抛出 ArgumentNullException,理论上,这可以进行单元测试。即便如此,测试的价值也很小。

        就个人而言,我几乎从不明确测试构造函数。如果它们复杂到需要测试,我倾向于觉得代码有点臭。

        【讨论】:

          【解决方案9】:

          如果构造函数包含一些逻辑,比如初始化一个类,我认为你应该测试这个构造函数。或者你可以告诉开发者,将初始化放在构造函数中会降低被测代码的可测试性。

          【讨论】:

            【解决方案10】:

            在许多受 FDA 监管的环境中,必须对更关键的代码进行 100% 测试...包括类的构建。因此,有时需要对构造函数进行测试,而不管是否要测试它们。此外,使用静态分析工具的公司将需要确保一个类的所有数据成员都正确初始化,以便没有错误,尽管代码可以顺利运行并且没有错误。通常数据成员初始化是在构造函数中完成的……只是值得深思。

            【讨论】:

            • 您有 FDA 要求的参考资料吗?
            【解决方案11】:

            测试访问器和修改器也是必要的,除非开发人员确保不能更改任何状态逻辑。例如,如果使用 Singleton 的设计模式,通常会使用访问器或属性,并且如果未初始化类,则从访问器完成,因为构造函数是私有的。在 C++ 中,可以将它们的函数设为 const 或 static,其中类的数据成员不能更改。 (注意:即使使用 static 也有一点风险,因为这些变量通常是全局的。)但是,如果没有测试,如果有人没有使用预防措施,你怎么能保证 100% 准确,写的东西不会成为失败时间?维护并非万无一失。

            【讨论】:

              【解决方案12】:

              视情况而定。

              我不会为您给出的示例如此简单的事情编写专用的构造函数测试。

              但是,如果您在构造函数中进行了逻辑测试,例如参数验证,那么可以,绝对可以。尽管像原始海报一样,如果可能的话,我不会在构造函数中进行任何工作,但通常必须进行参数验证。在这种情况下,不可避免地要阻止构造函数进行一些工作。如果构造函数中存在逻辑,则总是有可能出错,因此我将其视为任何其他方法调用并进行适当的测试。

              【讨论】:

                【解决方案13】:

                你绝对应该测试构造函数。如果你有一个默认构造函数,你应该测试它是否可以被调用。如果以后类被改变了——也许它变成了一个单例或者默认构造函数被删除以支持一个需要参数的类怎么办?在这种情况下,测试应该失败以提醒该更改(以便可以修复类或测试以满足新要求)。

                默认构造函数的存在是一个需要测试的要求。即使构造函数所做的只是设置将在其他地方测试的私有成员,也应该测试存在无参数构造函数的事实。

                【讨论】:

                • 这是一个古老的威胁,但我完全同意,应该测试默认构造函数,并且应该测试其他构造函数以在缺少所需参数时提供所需的行为。使用依赖注入,默认构造函数很可能会导致依赖的循环循环,如果您从不使用默认构造函数,您将永远不会看到。
                • 那里的所有示例(除了可能更改为单例,但在这种情况下甚至不应该有公共默认构造函数来测试)都会导致编译器错误。不仅不需要使用单元测试来测试无逻辑构造函数,而且这样做实际上是在浪费时间和金钱。如果构造函数具有逻辑(而不是简单地设置属性),那么绝对应该对其进行测试。同样@EtienneCharland 根据定义,默认构造函数没有依赖项(根本没有参数),因此它无法创建循环依赖循环。
                【解决方案14】:

                我认为答案是“是”。

                那里有大量的代码假设,可怕的是,一个初始化的对象状态,而不是一个空引用——通常是在构造函数中没有分配明确的值时。

                我很高兴在初始化的公共成员值发生更改时让构造函数测试中断以提醒我。这是关于防御性测试的 - 我很务实,并且比没有测试更快乐,当它们被证明无用或无用时将其删除。

                【讨论】:

                • 已经写好的为什么要删除?
                【解决方案15】:

                我正在测试包含逻辑的构造函数——例如验证或有条件地设置私有状态。验证错误最终会导致构造函数抛出异常。成功的执行最终会创建一个对象,该对象根据构造函数中设置的状态表现出特定的行为。 无论哪种方式,它都需要测试。但是构造函数测试很无聊,因为它们看起来都一样——调用构造函数,做出断言。测试方法声明通常比整个测试逻辑占用更多的空间......所以我写了一个简单的测试库来帮助为构造函数编写声明性测试:How to Easily Test Validation Logic in Constructors in C#

                这是一个示例,我在一个类的构造函数上尝试了七个测试用例:

                [TestMethod]
                public void Constructor_FullTest()
                {
                
                    IDrawingContext context = new Mock<IDrawingContext>().Object; 
                
                    ConstructorTests<Frame>
                        .For(typeof(int), typeof(int), typeof(IDrawingContext))
                        .Fail(new object[] { -3, 5, context }, typeof(ArgumentException), "Negative  length")
                        .Fail(new object[] { 0, 5, context }, typeof(ArgumentException), "Zero length")
                        .Fail(new object[] { 5, -3, context }, typeof(ArgumentException), "Negative width")
                        .Fail(new object[] { 5, 0, context }, typeof(ArgumentException), "Zero width")
                        .Fail(new object[] { 5, 5, null }, typeof(ArgumentNullException), "Null drawing context")
                        .Succeed(new object[] { 1, 1, context }, "Small positive length and width")
                        .Succeed(new object[] { 3, 4, context }, "Larger positive length and width")
                        .Assert();
                
                }
                

                通过这种方式,我可以为我的构造函数测试所有相关案例,而无需输入太多内容。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 2012-06-28
                  • 1970-01-01
                  • 2020-12-03
                  • 1970-01-01
                  • 2019-04-15
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多