2012-09-12 19 views
10

Làm thế nào tôi có thể kiểm tra đơn vị ContainsItalianVatinValidator trình xác thực tùy chỉnh, nhưng w * ithout truy cập vào vùng chứa * và dịch vụ validator (và do đó, tạo đối tượng gốc)?Kiểm tra đơn vị kiểm tra ràng buộc tùy chỉnh trong Symfony 2.1 nhưng không truy cập vào vùng chứa?

class ContainsItalianVatinValidator extends ConstraintValidator 
{ 
    /** 
    * @param mixed $value 
    * @param \Symfony\Component\Validator\Constraint $constraint 
    */ 
    public function validate($value, Constraint $constraint) 
    {  
     if (!preg_match('/^[0-9]{11}$/', $value, $matches)) { 
      $this->context->addViolation($constraint->message, array(
       '%string%' => $value 
      )); 
     } 

     // Compute and check control code 
     // ... 
    } 
} 

Trong trường hợp thử nghiệm của tôi tôi biết tôi nên truy cập vào ConstraintViolationList, nhưng tôi không biết làm thế nào để làm điều đó từ các validator bản thân:

class ContainsItalianVatinValidatorTest extends \PHPUnit_Framework_TestCase 
{ 
    public function testEmptyItalianVatin() 
    { 
     $emptyVatin = ''; 
     $validator = new ContainsItalianVatinValidator(); 
     $constraint = new ContainsItalianVatinConstraint(); 

     // Do the validation 
     $validator->validate($emptyVatin, $constraint); 

     // How can a get a violation list and call ->count()? 
     $violations = /* ... */; 

     // Assert 
     $this->assertGreaterThan(0, $violations->count()); 
    } 
} 
+0

Tôi sẽ trích xuất logic xác thực thành dịch vụ và viết kiểm tra đơn vị cho dịch vụ này. Bên trong lớp trình duyệt tính hợp lệ, bạn kiểm tra ràng buộc của mình với dịch vụ và thêm một thông báo nếu việc xác thực thất bại. Qua đó, logic xác thực của bạn không được kết hợp với khung công tác và mạnh mẽ hơn cho các thay đổi trong tương lai. – fabwu

Trả lời

20

Khi bạn có một cái nhìn tại lớp cha của trình xác nhận hợp lệ Symfony\Component\Validator\ConstraintValidator bạn thấy rằng có một phương thức được gọi là initialize có một đối tượng là Symfony\Component\Validator\ExecutionContext làm đối số.

Sau khi bạn tạo trình xác thực, bạn có thể gọi phương thức initialize và chuyển ngữ cảnh giả tới trình xác thực. Bạn không phải kiểm tra xem phương thức addViolation có hoạt động chính xác không, bạn chỉ phải kiểm tra xem nó có được gọi hay không và nếu nó được gọi với các tham số chính xác. Bạn có thể làm điều đó với chức năng giả của PHPUnit.

... 
$validator = new ContainsItalianVatinValidator(); 
$context = $this->getMockBuilder('Symfony\Component\Validator\ExecutionContext')-> disableOriginalConstructor()->getMock(); 

$context->expects($this->once()) 
    ->method('addViolation') 
    ->with($this->equalTo('[message]'), $this->equalTo(array('%string%', ''))); 

$validator->initialize($context); 

$validator->validate($emptyVatin, $constraint); 
... 

Trong mã này, bạn phải thay thế [tin nhắn] bằng tin nhắn được lưu trữ trong $constraint->message.

Thực ra, câu hỏi này liên quan nhiều hơn đến PHPUnit so với Symfony. Bạn có thể tìm thấy chương Test Doubles của tài liệu PHPUnit thú vị.

+0

Giải thích tuyệt vời. Điều duy nhất tôi không thể có được là tại sao, theo ý kiến ​​của bạn, đếm vi phạm là sai và tại sao tôi nên thích dựa vào thông điệp ràng buộc chính nó. Dù sao, +1. – gremo

+0

Tại sao bạn nên tính các vi phạm. Ít nhất trong mã trong câu hỏi của bạn chỉ có một cuộc gọi đến 'addViolation'. Nếu phương thức đó được gọi là một lần thì chính xác một vi phạm sẽ được thêm vào ngữ cảnh (các kiểm tra đơn vị của thử nghiệm Symfony2). –

+0

Nếu có nhiều cuộc gọi đến 'addViolation' hơn trong mã, bạn có thể thêm nhiều câu lệnh' $ context-> expected' trong đó mỗi lệnh gọi một hàm gọi khác là 'addViolation'. Đáng buồn là PHPUnit chỉ cung cấp hai phương thức để đếm số cuộc gọi của một phương thức 'once' và' any'. Tuy nhiên, [Mockery] (https://github.com/padraic/mockery) là thư viện mô phỏng tương thích với PHPUnit và có thể đếm số cuộc gọi phương thức trên đối tượng giả. –

10

Đã cập nhật cho Symfony 2.5+. Thêm kiểm tra cho từng thông báo có thể có rằng phương thức validate() trong trình xác thực của bạn có thể thêm với giá trị sẽ kích hoạt thông báo đó.

<?php 

namespace AcmeBundle\Tests\Validator\Constraints; 

use AcmeBundle\Validator\Constraints\SomeConstraint; 
use AcmeBundle\Validator\Constraints\SomeConstraintValidator; 

/** 
* Exercises SomeConstraintValidator. 
*/ 
class SomeConstraintValidatorTest extends \PHPUnit_Framework_TestCase 
{ 
    /** 
    * Configure a SomeConstraintValidator. 
    * 
    * @param string $expectedMessage The expected message on a validation violation, if any. 
    * 
    * @return AcmeBundle\Validator\Constraints\SomeConstraintValidator 
    */ 
    public function configureValidator($expectedMessage = null) 
    { 
     // mock the violation builder 
     $builder = $this->getMockBuilder('Symfony\Component\Validator\Violation\ConstraintViolationBuilder') 
      ->disableOriginalConstructor() 
      ->setMethods(array('addViolation')) 
      ->getMock() 
     ; 

     // mock the validator context 
     $context = $this->getMockBuilder('Symfony\Component\Validator\Context\ExecutionContext') 
      ->disableOriginalConstructor() 
      ->setMethods(array('buildViolation')) 
      ->getMock() 
     ; 

     if ($expectedMessage) { 
      $builder->expects($this->once()) 
       ->method('addViolation') 
      ; 

      $context->expects($this->once()) 
       ->method('buildViolation') 
       ->with($this->equalTo($expectedMessage)) 
       ->will($this->returnValue($builder)) 
      ; 
     } 
     else { 
      $context->expects($this->never()) 
       ->method('buildViolation') 
      ; 
     } 

     // initialize the validator with the mocked context 
     $validator = new SomeConstraintValidator(); 
     $validator->initialize($context); 

     // return the SomeConstraintValidator 
     return $validator; 
    } 

    /** 
    * Verify a constraint message is triggered when value is invalid. 
    */ 
    public function testValidateOnInvalid() 
    { 
     $constraint = new SomeConstraint(); 
     $validator = $this->configureValidator($constraint->someInvalidMessage); 

     $validator->validate('someInvalidValue', $constraint); 
    } 

    /** 
    * Verify no constraint message is triggered when value is valid. 
    */ 
    public function testValidateOnValid() 
    { 
     $constraint = new SomeConstraint(); 
     $validator = $this->configureValidator(); 

     $validator->validate('someValidValue', $constraint); 
    } 
} 
+0

Thật là một câu trả lời tuyệt vời! Cảm ơn nhiều! – cezar