如何最佳地对构造函数进行单元测试?

4

我有以下测试方法,用于测试构造函数的功能是否正常:

选项 #1

public function testConstructWorksProperly()
{
    $id = 1;
    $name = 'name';
    $foo = new Foo($id, $name);
    $this->assertEquals($id, $foo->getId());
    $this->assertEquals($name, $foo->getNome());
}

选项#2
public function testConstructWorksProperly()
{
    $id = 1;
    $name = 'name';
    $foo = new Foo($id, $name);
    $this->assertAttributeEquals($id, 'id', $foo);
    $this->assertAttributeEquals($name, 'name', $foo);
}

在选项#1中,我需要创建getter来断言构造函数是否正常工作,而在选项#2中,我使用一个断言来检查构造函数是否正确地设置了属性。
当我需要公开访问这些属性时,我想总是使用选项#1,因为这样可以节省时间和代码量,而不是编写另外两个测试用例用于getId和getName。
使用选项二似乎像是白盒测试。但是...
有一句话说:“每个测试一个断言”,因此,如果我的构造函数有6个参数,我将需要6个断言和6个getter来公开测试这些方法。
你会选择哪个选项?

除了选项之外,也许只有我会通过参数传递Foo的实例。 - Andrew
@Andrew,我正在测试Foo方法,而不是将其用作参数。 - Daniel Lima
2
除非你的setter方法有些特殊的操作,否则对getter和setter进行单元测试没有太大意义。 - scrowler
我使用反射来验证构造函数正确保存了内部值。这主要是为了测试确实需要以特定方式设置这些值的特定函数。正如@RobbieAverill上面所指出的,您并不总是需要测试这些内容。在我们的代码中,继承和其他因素意味着某些字段需要特定的数据,这就是为什么使用反射,以便我们可以验证保存的数据作为测试我们的共享库代码跨不同应用程序。 - Steven Scott
1个回答

6
在进行构造函数测试之前,你必须问自己:测试构造函数的目的是什么?你真正想通过这样做实现什么?
如果你想将每个方法隔离到单独的测试中,你应该选择选项#2(选项#1也会调用你的getter),但我真的认为,在“现实生活”项目中,测试构造函数没有价值。
构造函数只告诉对象应该如何构建,它不应该有太多逻辑,而且所有测试都依赖于构造函数,所以如果它不起作用,你的测试将失败。
如果构造函数中有逻辑,你可以使用命名构造函数使事情更简单(并且确保测试)。
附注1:不要忘记访问器并不好,你应该在添加它们之前三思(特别是setter)。我们应该始终专注于对象的行为而不是状态。
附注2:选项#2应该像这样:
public function testConstructWorksProperly()
{
    $id = 1;
    $name = 'name';
    $foo = new Foo($id, $name);
    $this->assertAttributeEquals($id, 'id', $foo);
    $this->assertAttributeEquals($name, 'name', $foo);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接