简体   繁体   English

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

[英]Testing constructors with multiple arguments using PHPUnit

Given the following Value object (no publicly accessible setters ): 给定以下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)
    {
        ...
    }
}

I have the following test class: 我有以下测试课:

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);
    }
}

As you can see I am currently using a DataProvider to supply my unit test with data. 如您所见,我当前正在使用DataProvider向单元测试提供数据。 Doing so results in a large array of values to be tested which are all written manually. 这样做会导致要测试的值的数组很大,这些值都是手动编写的。 Each argument is validated by an appropriate private method. 每个参数都通过适当的私有方法进行验证。 Currently to test those methods I am writing a data provider which contains valid and invalid argument values to test these methods (as below): 当前要测试这些方法,我正在编写一个数据提供程序,其中包含有效和无效的参数值来测试这些方法(如下所示):

// test validation on first argument
["invalid", "valid", "valid", "valid", "valid"],
...
// test validation on second argument
["valid", "invalid", "valid", "valid", "valid"],
...
// test validation on third argument
["valid", "valid", "invalid", "valid", "valid"],
...
// etc.

Is there something in PHPUnit I should be making use of that I have overlooked for this type of scenario? 对于这种情况,PHPUnit中是否有我应该利用的东西?

I agree with your comment that having validation logic in your value object is a matter of taste, but one major drawback is that it does make unit-testing harder, as you're seeing. 我同意您的意见,即在值对象中包含验证逻辑只是一个问题,但是一个主要缺点是,正如您所看到的,它确实使单元测试变得更加困难。 If your value object is now responsible for two things (data storage and validation), then testing gets more complicated, especially if you've made the validation logic private. 如果您的值对象现在负责两件事(数据存储和验证),则测试会变得更加复杂,尤其是在您将验证逻辑设为私有的情况下。

One approach you might want to consider is to use Reflection to test your private methods directly. 您可能要考虑的一种方法是使用Reflection直接测试您的私有方法。 You'll find a lot of debate about whether this is bad practice in some of the related questions linked to the side of this one, which I won't go over again here. 在与此问题相关的一些相关问题中,您会发现很多关于这种做法是否不好的争论,在此我不再赘述。 For my own point of view, I think this is one of the few cases where it makes sense. 以我自己的观点,我认为这是有意义的少数情况之一。

You can use something like this to run a private method from the unit test directly: 您可以使用以下方法直接从单元测试中运行私有方法:

/**
 * @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);
}

You could also combine the invalid parameters for all validation methods into a single dataProvider, and pass the method name in as an argument, to save duplicating the Reflection code. 您还可以将所有验证方法的无效参数组合到单个dataProvider中,并将方法名称作为参数传递,以节省重复的反射代码。

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

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

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