2013-02-09 17 views
7

Bạn có thể sử dụng $ this callback bên trong để nhận các thuộc tính được bảo vệ của lớp được giả lập trong phpunit không? Hoặc có cách nào khác để đạt được điều đó không?

$mock = $this->getMock('A', array('foo')); 
$mock->expects($this->any())->method('foo')->will(
    $this->returnCallback(function() { 
     return $this->bar; 
})); 

Điều này có thể thực sự hữu ích nếu bạn nghĩ về việc tiêm các đối tượng giả mạo. Đôi khi lớp có sự phụ thuộc mã hóa cứng cho lớp khác, nhưng nó tạo ra nó với phương thức mà bạn có thể giả tạo về mặt lý thuyết và tạo ra đối tượng giả lập thay vì đối tượng được mã hóa cứng. Hãy nhìn vào ví dụ khác.

class A { 
    protected $bar = "bar"; 

    public function foo() { 
    $b = new B(); 
    return $b->fizz($this->bar); 
    } 
} 

class B { 
    public function fizz ($buzz) { 
    return $buzz; 
    } 
} 

Nhưng hãy nói rằng lớp B làm điều gì đó xấu và tôi muốn thay thế bằng mô hình.

$mockB = $this->getMock('B'); 
// (...) - and probably mock other things 
$mockA = $this->getMock('A', array('foo')); 
$mockA->expects($this->any())->method('foo')->will(
    $this->returnCallback(function() use ($mockB) { 
     return $mockB->fizz($this->bar); 
})); 

Điều này có thể đạt được bằng cách nào đó không?

Dĩ nhiên mà không cần bất kỳ sự ngạc nhiên, hiện nay, nếu tôi làm điều này như trên sau đó tôi nhận được lỗi:

PHP Fatal error: Using $this when not in object context in (...) 

Sử dụng use từ khóa Tôi có thể kế thừa $ mockA từ phạm vi cha mẹ:

$mockB = $this->getMock('B'); 
// (...) - and probably mock other things 
$mockA = $this->getMock('A', array('foo')); 
$mockA->expects($this->any())->method('foo')->will(
    $this->returnCallback(function() use ($mockA, $mockB) { 
     return $mockB->fizz($mockA->bar); 
})); 

nhưng bằng cách này, tôi sẽ cố gắng truy cập vào thanh công khai và tôi sẽ nhận được:

PHP Fatal error: Cannot access protected property (...) 

Trả lời

0

S Ince php 5.4 bạn có thể sử dụng $this trong đóng cửa, nhưng bạn phải trả lại gọi lại này từ đối tượng, có chứa các đặc tính bảo vệ:

class A { 
    protected $bar = "bar"; 

    public function foo() { 
     $b = new B(); 
     return $b->fizz($this->bar); 
    } 

    public function getCallback(B $b) { 
     return function() use($b) { 
      return $b->fizz($this->bar); 
     }; 
    } 
} 

class B { 
    public function fizz ($buzz) { 
     return $buzz; 
    } 
} 

$mockA = new A; 
$mockB = new B; 

$callBack = $mockA->getCallback($mockB); 
var_dump($callBack() === $mockA->foo()); 

Tuy nhiên, nếu bạn cần để có được giá trị tài sản được bảo vệ, bạn cần xác định getter công cộng cho nó. Bằng cách này, thử nghiệm cũng sẽ hoạt động trong php 5.3

+0

Ừ Tôi biết tôi có thể xác định getter nào nhưng cho phép nói rằng nó là một thư viện bên ngoài hoặc bạn chỉ muốn giữ nó được bảo vệ. Toàn bộ câu hỏi là về cách để có được các thuộc tính được bảo vệ của lớp trong trường hợp đã cho. Vì vậy, đối với php 5.3 câu trả lời vẫn không, bạn không thể làm điều đó. Đối với php 5.4, tuyệt vời bạn có thể sử dụng $ this! Đó là bước tiến lớn! nhưng tôi vẫn cần phải thay đổi lớp mà tôi không muốn thay đổi. Bạn có thể ít nhất tạo lớp mở rộng A để thêm 'getCallback (B $ b)'? Cần kiểm tra điều đó. –

1

Như được chỉ ra bởi dev-null-dweller, trong PHP 5.4, bạn có thể sử dụng $ this trong phần đóng như thể bạn đang làm việc bình thường trong phương thức.

Trong 5.3, bạn có thể bắt chước hành vi này bằng cách thực hiện:

public function getCallback(B $b) { 
    $self = $this; 
    return function() use($b, $self) { 
     return $b->fizz($self->bar); 
    }; 
} 
7

Như câu trả lời khác đã chỉ ra, $this có thể được sử dụng trong đóng cửa kể từ PHP 5.4. Một thực tế ít được biết đến là bạn có thể ràng buộc một đóng cửa cho các đối tượng tùy ý và trên thực tế làm cho tài sản riêng của họ có thể truy cập như thế. Phương thức bạn cần là bindTo(), trả về một đóng cửa mới với ngữ cảnh khác.

$cb = function() { 
    return $this->bar; 
}; 
$cb = $cb->bindTo($mockA); 

Hoặc chính xác hơn, ví dụ của bạn sẽ trông như thế:

$mockB = $this->getMock('B'); 
// (...) - and probably mock other things 
$mockA = $this->getMock('A', array('foo')); 
$fooCallback = function() use (&$mockB) { 
    return $mockB->fizz($this->bar); 
}; 
$mockA->expects($this->any())->method('foo')->will(
    $this->returnCallback($fooCallback->bindTo($mockA))); 
+1

+1 để đề cập đến tính năng này. PHPUnit cũng khuyến khích sử dụng PHP 5.4, vì vậy đây có thể là thời điểm tốt để bắt đầu chuyển đổi. (cũng lưu ý đến bản thân ... :)) – qrazi

Các vấn đề liên quan