一个简单的PHPUnit测试助手

2.4.1 2022-08-01 12:31 UTC

README

测试助手库

这个库是PHP应用程序测试的简单助手,它提供旨在涉及构造函数测试、获取器和设置器等的函数...

基本用法

测试助手库旨在自动化基本逻辑元素的测试。这些基本元素将是简单的构造函数测试、获取器/设置器或更抽象的访问器测试,最后是无要求的实例构造。

要使用它,测试用例必须扩展AbstractTestClass类,并定义测试的类。

class MyClassTest extends AbstractTestClass
{
    /**
     * Get tested class
     *
     * Return the tested class name
     *
     * @return string
     */
    protected function getTestedClass(): string
    {
        return ConfigurationDefinition::class;
    }
}

构造函数测试

首先进行构造函数测试。为了验证实例的构造,有必要测试多个案例

  • 参数分配
  • 参数默认值

为此,assertConstructor()方法允许作为数组的两个参数。第一个数组将包含预期分配的属性名称作为键,分配的值作为值,代表调用中提供的参数的顺序相同。第二个数组将具有相同的结构,并代表属性的默认值。

要测试作为引用的值分配(对于对象很有用),则属性必须以same:关键字为前缀。

class MyClassTest extends AbstractTestClass
{
    /**
     * Test constructor
     *
     * This method validate the constructor of the MyClass class.
     *
     * @return void
     */
    public function testConstruct()
    {
        $this->assertConstructor(
            [
                'theFirstArgument' => 'theArgumentValue',
                'same:reference' => $this->createMock(\stdClass::class),
                'same:otherReference' => $this->createMock(\stdClass::class)
            ]
        );
        
        $this->assertConstructor(
            [
                'theFirstArgument' => 'theArgumentValue',
                'same:reference' => $this->createMock(\stdClass::class)
            ],
            [
                'otherReference' => null
            ]
        );
    }
    
    [...]
}

自版本2.3起

也可以通过提供用户定义的约束(使用KairosProject\Constrait\InjectionConstraint类)来执行更复杂的断言。

以下是一个示例,说明在将属性存储在属性中之前,构造函数如何通过数组注入数据

class MyClassTest extends AbstractTestClass
{
    /**
     * Test constructor
     *
     * This method validate the constructor of the MyClass class.
     *
     * @return void
     */
    public function testConstruct()
    {
        $this->assertConstructor(
            [
                'modifiedInjection' => new InjectionConstraint(true, $this->equalTo([true]))
            ]
        );
    }
    
    [...]
}

访问受保护的或私有方法

要访问私有或受保护的私有方法,可以使用getClassMethod('name')。它将返回一个配置为默认可访问的ReflectionMethod实例。

class MyClassTest extends AbstractTestClass
{
    /**
     * Test method
     *
     * This method validate the methodName method of the MyClass class.
     *
     * @return void
     */
    public function testMethodName()
    {
        $instance = $this->getInstance();
        $this->assertProtectedMethod('methodName');
        $method = $this->getClassMethod('methodName');

        $result = $method->invoke($instance);

        $this->assertEquals('The expected result', $result);
    }
    
    [...]
}

测试简单的获取器或设置器

简单的获取器和设置器是访问对象中私有或受保护的属性的一种常见做法。为了验证属性是否正确分配或返回,可以使用assertIsSimpleGetter('property', 'getterMethodName', 'value')assertIsSimpleSetter('property', 'getterMethodName', 'value')方法。

如果您想同时测试这两个,则可以使用assertHasSimpleAccessor('property', 'value')方法。

class MyClassTest extends AbstractTestClass
{
    /**
     * Test getProperty.
     *
     * This method validate the getProperty method of the MyClass class.
     *
     * @return void
     */
    public function testGetProcessEvent()
    {
        $propertyContent = $this->createMock(\stdClass::class);
        $this->assertIsSimpleGetter(
            'property',
            'getProperty',
            $propertyContent
        );
    }

    /**
     * Test for property accessor.
     *
     * Validate the getProperty and setProperty methods.
     *
     * @return void
     */
    public function testPropertyAccessor() : void
    {
        $this->assertHasSimpleAccessor('property', $this->createMock(\stdClass::class));
    }
    
    [...]
}

获取未调用构造函数的新实例

由于我们不希望在每次测试时都测试构造函数,因此可以通过调用getInstance()方法来获取一个新的实例。此方法的可选参数可以用于直接在属性中设置依赖项,使用ReflectionProperty而不是构造函数或设置器。

class MyClassTest extends AbstractTestClass
{
    /**
     * Test for process.
     *
     * Validate the process methods of the MyClass class.
     *
     * @return void
     */
    public function testProcess()
    {
        $instance = $this->getInstance(
            [
                'logger' => $this->createMock(LoggerInterface::class),
                'eventDispatcher' => $this->createMock(EventDispatcher::class)
            ]
        );

        $this->assertEquals(42, $instance->process());
    }
    
    [...]
}

获取调用构建器以配置模拟调用

要创建新的InvocationBuilder实例,可以使用getInvocationBuilder($mock, new Invocation(), 'methodName')。此方法只是$mock->expect($count)->method('methodName')的一个辅助程序;

class MyClassTest extends AbstractTestClass
{
    /**
     * Test for routine.
     *
     * Validate the routine methods of the MyClass class.
     *
     * @return void
     */
    public function testRoutine()
    {
        $logger = $this->createMock(LoggerInterface::class);
        $this->getInvocationBuilder($logger, $this->once(), 'debug')
                ->withConsecutive(
                    [
                        $this->equalTo('Start routine')
                    ],
                    [
                        $this->equalTo('End routine')
                    ]
                );
        
        $this->getInstance(['logger' => $logger])->routine();
    }
    
    [...]
}

批量属性内容验证

要验证一组属性存储的值,请使用assertPropertiesSameassertPropertiesEqual方法。

class MyClassTest extends AbstractTestClass
{
    /**
     * Test set data
     *
     * Validate the data setter of some class
     *
     * @return void
     */
    public function testContent(): void
    {
        $instance = $this->getInstance();
        $date = new \DateTime();
        
        $instance->setSubject('Test subject');
        $instance->setEmail('matthieu.vallance@exemple.org');
        $instance->setDate($date);

        $this->assertPropertiesSame($instance, ['date' => $date]);
        $this->assertPropertiesEqual(
            $instance, 
            ['subject' => 'Test subject', 'email' => 'matthieu.vallance@exemple.org']
        );
    }
    
    [...]
}

在运行时设置测试类

在某些特定情况下,您可能需要定义在运行时测试的类。为此目的,可以使用runTestWithInstanceOf方法。

class MyClassTest extends AbstractTestClass
{
    /**
     * Test a specific class outside the defined tested class
     *
     * Validate a class defined at runtime
     *
     * @return void
     */
    public function testSpecificClass(): void
    {
        $baseInstance = $this->getInstance();
        $this->runTestWithInstanceOf(SUT::class);
        $specificInstance = $this->getInstance();
        
        $this->assertInstanceOf($this->getTestedClass(), $baseInstance);
        
        $this->assertInstanceOf(SUT::class, $specificInstance);
    }
    
    [...]
}