使用PHPUnit测试具有多个参数的构造函数

时间:2017-09-20 10:29:57

标签: php unit-testing testing phpunit

鉴于以下 DEPT CURR_DATE DIFFERENCE -------------------- --------- ---------- A 30-SEP-17 26 B 30-JUL-17 8 B 30-SEP-17 13 C 30-AUG-17 19 D 30-SEP-17 13 D 30-DEC-17 13 R 30-SEP-17 13 对象(无法公开访问Value):

setters

我有以下测试类:

class Address
{
    public function __construct(string $addressLine1, string $addressLine2 = null, string $town, string $county, PostcodeInterface $postcode)
    {
        if (!$this->validateAddressLine($addressLine1)) {
            throw new \InvalidArgumentException(...);
        }
        $this->addressLine1 = $this->normaliseAddressLine($addressLine1);
        ...
    }

    private function validateAddressLine(string $addressLine)
    {
        ...
    }

    private function normaliseAddressLine(string $addressLine)
    {
        ...
    }
}

正如您所看到的,我正在使用class AddressTest extends PHPUnit\Framework\TestCase { public function invalidConstructorArgs() { return [ ['1/1 Greenbank Road', '%$', 'city', 'county', new Postcode('123FX')] ]; } /** * @test * @dataProvider invalidConstructorArgs */ public function constructor_with_invalid_args_throws_exception($addressLine1, $addressLine2, $town, $county, $postcode) { $this->expectedException(\InvalidArgumentException::class); $address = new Address($addressLine1, $addressLine2, $town, $county, $postcode); } } 为我的单元测试提供数据。这样做会导致要测试的大量值,这些值都是手动编写的。每个参数都由适当的私有方法验证。目前为了测试这些方法,我正在编写一个包含有效和无效参数值的数据提供程序来测试这些方法(如下所示):

DataProvider

PHPUnit中有什么东西我应该利用我忽略的这种情况吗?

1 个答案:

答案 0 :(得分:1)

我同意你的意见,即在你的价值对象中使用验证逻辑是一个品味问题,但一个主要的缺点是它确实使单元测试变得更加困难,正如你所看到的那样。如果您的值对象现在负责两件事(数据存储和验证),那么测试会变得更复杂,特别是如果您已将验证逻辑设置为私有。

您可能需要考虑的一种方法是使用Reflection直接测试您的私有方法。你会发现很多关于这是否与这个方面相关的一些相关问题的不良做法的争论,我不会在这里再说一遍。就我个人而言,我认为这是少数几个有意义的案例之一。

您可以使用类似的东西直接从单元测试运行私有方法:

/**
 * @test
 * @dataProvider invalidAddressLine1Provider
 */
public function invalid_parameter_throws_exception($invalidParameter)
{
    $reflector = new \ReflectionClass(Foo::class);
    // The call to newInstanceWithoutConstructor here is important, since the
    // constructor is what we're looking to avoid
    $instance = $reflector->newInstanceWithoutConstructor();

    $method = $reflector->getMethod('validateAddressLine1');
    $method->setAccessible(true);

    $this->expectException(\Exception::class);
    $method->invoke($instance, $invalidParameter);
}

您还可以将所有验证方法的无效参数组合到单个dataProvider中,并将方法名称作为参数传递,以保存重复的反射代码。

public function invalidProvider()
{
    return [
        ['validateAddressLine1', 'invalid value for line 1'],
        ['validateAddressLine2', 'invalid value for line 2'],
        // ...
    ];
}