简体   繁体   English

单元测试构造函数是否重要?

[英]Is it important to unit test a constructor?

Ought I to unit test constructors? 我应该对单元测试构造函数吗? Say I have a constructor like this: 说我有这样的构造函数:

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

Do I need to write a unit test for this construtor? 我是否需要为这位建筑师编写单元测试? I don't have any getters for the wrapper variable, so I don't need to test that. 我没有包装器变量的任何getter,所以我不需要测试它。

Unit testing is about testing the public states, behaviors, and interactions of your objects. 单元测试是关于测试对象的公共状态,行为和交互。

If you simply set a private field in your constructor, what is there to test? 如果您只是在构造函数中设置一个私有字段,那么有什么要测试的?

Don't bother unit-testing your simple accessors and mutators. 不要打扰单元测试你的简单访问器和mutator。 That's just silly, and it doesn't help anyone. 那太傻了,对任何人都没有帮助。

Yes. 是。 If you have logic in your constructor, you should test it. 如果构造函数中有逻辑,则应测试它。 Simply setting properties is not logic IMO. 简单地设置属性不是逻辑IMO。 Conditionals, control flow, etc IS logic. 条件,控制流等IS逻辑。

Edit: You should probably test for when IMapinfoWrapper is null, if that dependency is required. 编辑:您应该测试IMapinfoWrapper何时为null,如果需要该依赖项。 If so, then that is logic and you should have a test that catches your ArgumentNullException or whatever... your tests are specifications that define how the code behaves. 如果是这样,那么这就是逻辑,你应该有一个测试来捕获你的ArgumentNullException或者其他......你的测试是定义代码行为方式的规范。 If it throws an ArgumentNullException, then that should be specified in a test. 如果它抛出ArgumentNullException,那么应该在测试中指定。

Q: If you are setting a member variable in the constructor, why are you setting it. 问:如果要在构造函数中设置成员变量,为什么要设置它。

A: Because you have a failing unit test that can only be made to pass by setting it in the constructor. 答:因为你有一个失败的单元测试,只能通过在构造函数中设置它来传递。

If you use this logic, where you only write code to cause a unit test to pass (Test Driven Development), then you will already have the answer to your question. 如果您使用此逻辑,只编写代码以使单元测试通过(测试驱动开发),那么您已经有了问题的答案。

不会。它的功能将通过课堂上的每个其他单元测试进行测试。

You absolutely should test the constructor. 你绝对应该测试构造函数。 If you have a default constructor, you should test that it can be called. 如果您有默认构造函数,则应测试是否可以调用它。 What if later on the class is changed--perhaps it becomes a singleton or the default constructor is removed in favor of one requiring parameters? 如果稍后更改类会怎么样 - 也许它会变成单例或删除默认构造函数而不是需要参数? The test should fail in that case to alert that change (so that the class or the test can be fixed to meet the new requirement). 在这种情况下,测试应该失败以警告该更改(以便可以修复类或测试以满足新要求)。

The presence of a default constructor is a requirement that should have a test. 默认构造函数的存在是应该进行测试的要求。 Even if all the constructor does is set private members that will be tested elsewhere, the fact that there is a parameterless constructor should be tested. 即使所有构造函数都设置了将在其他地方测试的私有成员,也应测试存在无参数构造函数的事实。

I am testing constructors when they contain logic - eg validation or conditional setting a private state. 我在构造函数包含逻辑时进行测试 - 例如验证或条件设置私有状态。 Validation errors end up in an exception thrown from the constructor. 验证错误最终在构造函数抛出的异常中结束。 Successful execution ends up in a creation of object which exhibits specific behavior depending on the state which was set in the constructor. 成功执行最终会创建一个对象,该对象根据构造函数中设置的状态显示特定的行为。 In either way, it requires testing. 无论哪种方式,都需要测试。 But constructor tests are boring because they all look the same - invoke the constructor, make an assertion. 但构造函数测试很无聊,因为它们看起来都一样 - 调用构造函数,进行断言。 Test method declarations often take more space than the whole testing logic... So I wrote a simple testing library which helps write declarative tests for the constructors: How to Easily Test Validation Logic in Constructors in C# 测试方法声明通常占用比整个测试逻辑更多的空间......所以我写了一个简单的测试库,它有助于为构造函数编写声明性测试: 如何在C#中轻松测试构造函数中的验证逻辑

Here is an example in which I am trying seven test cases on a constructor of one class: 这是一个例子,我在一个类的构造函数上尝试七个测试用例:

[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();

}

In this way, I can test all the relevant cases for my constructor without typing much. 通过这种方式,我可以测试我的构造函数的所有相关情况,而无需输入太多内容。

In many FDA-regulated environments, more critical code must be 100% tested...including the construction of classes. 在许多由FDA监管的环境中,更关键的代码必须经过100%的测试......包括类的构建。 So, the testing of constructors is sometimes necessary regardless of reasoning to or not to test them. 因此,无论是否进行测试,都需要对构造函数进行测试。 Also, companies that use static analysis tools will need to make sure ALL data members of a class are properly initialized in order to not have errors although the code may run smoothly and free of errors. 此外,使用静态分析工具的公司需要确保正确初始化类的所有数据成员,以便不会出现错误,尽管代码可以顺利运行且没有错误。 Usually data member initialization is done in the constructor...just food for thought. 通常数据成员初始化在构造函数中完成...只是值得深思。

It depends. 这取决于。

I wouldn't bother writing a dedicated constructor test for something so simple as the example you gave. 我不打算为你给出的例子这么简单的东西写一个专门的构造函数测试。

However, if you have logical tests in the constructor such as a parameter validation, then yes, absolutely. 但是,如果您在构造函数中进行了逻辑测试,例如参数验证,那么绝对是。 Although, like the original poster, I do no work in the constructor if possible, it's common that parameter validation must be done. 虽然像原始海报一样,如果可能,我在构造函数中不起作用,但通常必须进行参数验证。 In this case it is unavoidable to keep the constructor from doing some work. 在这种情况下,不可避免地使构造函数不做某些工作。 If there's logic in the constructor, there's always the chance that it could be wrong, hence I treat it just like any other method call and test it appropriately. 如果构造函数中存在逻辑,那么它总是存在错误的可能性,因此我将其视为任何其他方法调用并对其进行适当测试。

I think the answer to this is "Yes". 我认为答案是肯定的。

There's plenty of code out there which assumes, horribly, an initialised object state, instead of a null reference - often when there are no explicit values assigned in the constructor. 有很多代码可以假设一个初始化的对象状态,而不是空引用 - 通常在构造函数中没有指定显式值时。

I am happy to have constructor tests break to alert me when initialised public member values have been changed. 我很高兴在初始化的公共成员值被更改时,构造函数测试会中断以提醒我。 This is about defensive testing - I'm pragmatic, and happier to have tests than not, and remove them when they're shown to not be helpful or useful. 这是关于防御性测试 - 我是务实的,并且更乐于接受测试,并且当它们被证明没有帮助或有用时将它们移除。

Testing accessors and mutators is also necessary unless the developer has ensured that no state logic can be changed. 除非开发人员确保不能更改状态逻辑,否则还必须测试访问器和更改器。 For instance, if one uses the design pattern for a Singleton, often times accessors or properties are used, and if the class is not initialized, it is done from the accessor since the constructor is private. 例如,如果使用Singleton的设计模式,通常会使用访问器或属性,如果未初始化类,则由访问器完成,因为构造函数是私有的。 In C++, one can make their functions const or static in which data members of the class cannot be changed. 在C ++中,可以使其函数为const或static,其中类的数据成员不能更改。 (Note: Even using static is a bit risky since those variables are often global.) However, without a test, if someone fails to use preventative measures, how can you guarantee with a 100% accuracy that what is written cannot become a failure over time? (注意:即使使用静态也有点风险,因为这些变量通常是全局的。)但是,如果没有测试,如果有人未能使用预防措施,您如何能够100%准确地保证所写的内容不会成为故障时间? Maintenance is hardly foolproof. 维护很难万无一失。

I believe in 100% coverage. 我相信100%的报道。 Also 100% coverage not by simply testing simple interactions by mocking things or just setting and getting things, but more integration/acceptance tests that check functionality. 此外,100%的覆盖范围不仅仅是通过模拟事物或只是设置和获取事物来测试简单的交互,而是通过检查功能的更多集成/验收测试。 So if you end up writing really good integration/acceptance tests, all of your constructors (and simple methods such as setters and getters) should be called. 因此,如果您最终编写了非常好的集成/验收测试,则应调用所有构造函数(以及诸如setter和getter之类的简单方法)。

What behavior of an instance of SystemInfo depends on the value of wrapper ? SystemInfo实例的行为取决于wrapper的值?

If anything can go wrong (eg null value causes breakage) then I suggest writing scenarios describing each such situation and using them to drive the definition of the appropriate unit tests. 如果出现任何问题(例如,空值导致破损),那么我建议编写描述每种情况的场景,并使用它们来推动相应单元测试的定义。

If all of the scenarios end up being dependent on the state or behavior of the private instance of IMapinfoWrapper , then I'd suggest writing unit tests on that class instead. 如果所有场景最终都依赖于IMapinfoWrapper的私有实例的状态或行为,那么我建议在该类上编写单元测试。

Not unless you're writing a compiler, because you would only be testing that the compiler could generate code to do assignments, which is normally pointless. 除非您正在编写编译器,否则您只会测试编译器是否可以生成执行分配的代码,这通常是没有意义的。

Now, in a more usual situation, if you want to do something else with the wrapper, then maybe there's a point. 现在,在更常见的情况下,如果你想用包装器做其他事情,那么也许有一点。 For example, you could throw an ArgumentNullException if you tried to pass a null, and in theory, that could have a unit test. 例如,如果您尝试传递null,则可以抛出ArgumentNullException,理论上,可以进行单元测试。 Even then, the value of the test is pretty minimal. 即使这样,测试的价值也非常小。

Personally, I almost never explicitly test constructors. 就个人而言,我几乎从不明确地测试构造函数。 If they are complicated enough to need tests, I tend to feel the code is a little on the smelly side. 如果它们足够复杂以至于需要测试,我倾向于觉得代码有点臭的一面。

If the constructor contains some logic, such as initialize a class, I think you should test the very constructor. 如果构造函数包含一些逻辑,比如初始化一个类,我认为你应该测试一个非常构造函数。 Or you can talk to the developer that putting initialization in the constructor will cut the testability of code under test. 或者您可以与开发人员讨论在构造函数中放置初始化会降低测试代码的可测试性。

Unit Testing is about checking paths of execution, something often refered as Cyclomatic Complexity 单元测试是关于检查执行路径,通常称为Cyclomatic Complexity

If you have no path to choose from, no if, no loop, no GOTO (=P) its not very useful 如果你没有可供选择的路径,没有if,没有循环,没有GOTO(= P)它不是很有用

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM