PHPUnit Symfony4创建和测试自定义验证器

时间:2018-07-04 18:36:21

标签: regex phpunit symfony4 customvalidator

我的目标是建立自定义验证程序并使用PHPUnit对其进行测试。

PasswordComplexity.php

<?php

namespace App\Components\Validator\Constraint;

use Symfony\Component\Validator\Constraints\Regex;

/**
 * Class Password
 * @package App\Components\Validator\Constraint
 * @Annotation
 */
class PasswordComplexity extends Regex
{
    public $message = 'The password "{{string}}" does not meet the password policy requirements.';
    public $pattern = [
        '/.{8,}/',
        '/\d+/',
        '/[a-z]+/',
        '/[A-Z]+/',
        '/[!@#$%\-_*+=]+/'
    ];

    /**
     * {@inheritdoc}
     */
    public function getRequiredOptions()
    {
        return [];
    }
}

PasswordComplexityValidator.php

<?php
declare(strict_types=1);
namespace App\Components\Validator\Constraint;

use Symfony\Component\Validator\Constraints\RegexValidator;

class PasswordComplexityValidator extends RegexValidator
{
}

PasswordComplexityValidatorTest.php

<?php
declare(strict_types=1);
namespace App\Tests\Components\Validator\Constraint;

use App\Components\Validator\Constraint\PasswordComplexity;
use App\Components\Validator\Constraint\PasswordComplexityValidator;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Validator\Context\ExecutionContext;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Symfony\Component\Validator\Violation\ConstraintViolationBuilderInterface;

class PasswordComplexityValidatorTest extends TestCase
{
    public function testRequiredOptions()
    {
        $validator = new PasswordComplexity();
        $this->assertEquals([], $validator->getRequiredOptions());
    }

    /**
     * @dataProvider validPasswordProvider
     */
    public function testValidation($value)
    {
        $constraint = new PasswordComplexity();

        /** @var ExecutionContextInterface|MockObject $context */
        $context = $this->getMockExecutionContext();
        $context->expects($this->never())->method('buildViolation');

        $validator = new PasswordComplexityValidator();
        $validator->initialize($context);

        $validator->validate($value, $constraint);
    }

    /**
     * @dataProvider invalidPasswordProvider
     */
    public function testValidationFail($value)
    {
        $constraint = new PasswordComplexity();

        /** @var ExecutionContextInterface|MockObject $context */
        $context = $this->getMockExecutionContext();
        $context->expects($this->once())
            ->method('buildViolation')
            ->with($constraint->message)
            ->willReturn($this->getMockConstraintViolationBuilder());

        $validator = new PasswordComplexityValidator();
        $validator->initialize($context);
        $validator->validate($value, $constraint);
    }

    public function validPasswordProvider()
    {
        return [
            ['jXb8p$cn'],
            ['!MC6NcuS'],
            ['!3%Sy6iF'],
            ['XyiWmw2#'],
            ['r@5e#hSY'],
        ];
    }

    public function invalidPasswordProvider()
    {
        return [                // conditions not meet:
            ['YQ(GJ)&'],        // at least 8 characters
            ['LuAqA=uX'],       // at least 1 digit (0 to 9)
            ['{OG>:@I1'],       // at least 1 small case letter
            [']su-(+\a'],       // at least 1 upper case letter
            ['CJuCyAGc'],       // at least 1 special character
        ];
    }

    private function getMockExecutionContext()
    {
        $context = $this->getMockBuilder(ExecutionContext::class)
            ->disableOriginalConstructor()
            ->getMock();
        return $context;
    }

    private function getMockConstraintViolationBuilder()
    {
        $constraintViolationBuilder = $this->getMockBuilder(ConstraintViolationBuilderInterface::class)->getMock();
        $constraintViolationBuilder
            ->method('setParameter')
            ->willReturn($constraintViolationBuilder);
        $constraintViolationBuilder
            ->method('setCode')
            ->willReturn($constraintViolationBuilder);
        $constraintViolationBuilder
            ->method('addViolation');
        return $constraintViolationBuilder;
    }

我有以下错误:

这是10个错误:

  

\ Tests \ Components \ Validator \ Constraint \ PasswordComplexityValidatorTest :: testValidation与数据集#0('jXb8p $ cn')   preg_match()期望参数1为字符串,给定数组

我不知道如何将数组样本传递给验证器。

1 个答案:

答案 0 :(得分:0)

我已经解决了这个问题。

我的正则表达式模式与示例数据不匹配,更改为:

public function invalidPasswordProvider()
{
    return [                // conditions not meet:
        ['J#$j#2'],        // at least 8 characters
        ['LuAqA=uX'],       // at least 1 digit (0 to 9)
        ['$OG3W4I1'],       // at least 1 small case letter
        ['3su-a+aa'],       // at least 1 upper case letter
        ['CJuC$AGc'],       // at least 1 special character
    ];
}

Pattern变量已从PasswordComplexity.php中删除

public $pattern = [
    '/.{8,}/',
    '/\d+/',
    '/[a-z]+/',
    '/[A-Z]+/',
    '/[!@#$%\-_*+=]+/'
];

PasswordComplexityValidator.php已充满验证功能和所有必要数据:

<?php
declare(strict_types=1);
namespace App\Components\Validator\Constraint;

use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Constraints\RegexValidator;

class PasswordComplexityValidator extends RegexValidator
{
    const PASS_REQ_1 = '/.{8,}/';           // at least 8 characters
    const PASS_REQ_2 = '/\d+/';             // at least 1 digit (0 to 9)
    const PASS_REQ_3 = '/[a-z]+/';          // at least 1 small case letter
    const PASS_REQ_4 = '/[A-Z]+/';          //  at least 1 upper case letter
    const PASS_REQ_5 = '/[!@#$%\-_*+=]+/';  //  at least 1 special character (e.g.: !@#$%-_*+=)

    public static $patterns = [
        self::PASS_REQ_1,
        self::PASS_REQ_2,
        self::PASS_REQ_3,
        self::PASS_REQ_4,
        self::PASS_REQ_5,
    ];

    public function validate($password, Constraint $constraint)
    {
        foreach (self::$patterns as $pattern) {
            if (!preg_match($pattern, $password, $matches))
            {
                $this->context
                    ->buildViolation($constraint->message)
                    ->setParameter('{{ string }}', $password)
                    ->addViolation();
            }
        }
    }
}

也更改了

/**
 * @dataProvider invalidPasswordProvider
 */
public function testValidationFail($value)
{
    $constraint = new PasswordComplexity();

    /** @var ExecutionContextInterface|MockObject $context */
    $context = $this->getMockExecutionContext();
    $context->expects($this->once())

$context->expects($this->atLeast(1))

如果密码不匹配多个正则表达式,则进行测试。

测试通过了,我觉得现在一切都应该正常了。

关于, J