2010-03-01 51 views
38

Tôi đang tìm cách tốt nhất để đi về thử nghiệm các phương pháp sau đây tĩnh (đặc biệt là sử dụng một mô hình học thuyết):PHPUnit Mock Objects và phương pháp tĩnh

class Model_User extends Doctrine_Record 
{ 
    public static function create($userData) 
    { 
     $newUser = new self(); 
     $newUser->fromArray($userData); 
     $newUser->save(); 
    } 
} 

Lý tưởng nhất, tôi sẽ sử dụng một đối tượng giả để đảm bảo rằng "fromArray" (với dữ liệu người dùng được cung cấp) và "save" được gọi, nhưng điều đó là không thể vì phương thức này là tĩnh.

Mọi đề xuất?

Trả lời

42

Sebastian Bergmann, tác giả của PHPUnit, gần đây đã có một bài đăng trên blog về số Stubbing and Mocking Static Methods. Với PHPUnit 3.5 và PHP 5.3 cũng như sử dụng nhất quán cuối tĩnh ràng buộc, bạn có thể làm

$class::staticExpects($this->any()) 
     ->method('helper') 
     ->will($this->returnValue('bar')); 

Cập nhật:staticExpectsdeprecated as of PHPUnit 3.8 và sẽ được loại bỏ hoàn toàn với các phiên bản sau.

+11

Worth lưu ý " Cách tiếp cận này chỉ làm việc cho các stubbing và mocking của các cuộc gọi phương thức tĩnh, nơi người gọi và callee trong cùng một lớp. cái chết để kiểm tra] (http://misko.hevery.com/2008/12/15/static-methods-are-death-to-testability/). " –

+1

Hàm 'staticExpects' đã bị xóa khỏi PHPUnit v4. Xem [chủ đề này trên github] (https://github.com/sebastianbergmann/phpunit-mock-objects/issues/137) để giải thích lý do. –

+4

Như chúng ta đã biết rằng 'staticExpects' đã được xóa hoàn toàn khỏi phiên bản PHPUnit gần đây, thì cách thay thế nào để đạt được điều này mà không có' staticExpects'? –

0

Phương pháp tĩnh thử nghiệm thường được coi là hơi khó (như bạn có thể đã nhận thấy), đặc biệt là trước PHP 5.3.

Bạn không thể sửa đổi mã của mình để không sử dụng phương pháp tĩnh? Tôi không thực sự thấy tại sao bạn đang sử dụng một phương pháp tĩnh ở đây, trên thực tế; điều này có lẽ có thể được viết lại cho một số mã không tĩnh, phải không?


Ví dụ, có thể một cái gì đó như thế này không làm các trick:

class Model_User extends Doctrine_Record 
{ 
    public function saveFromArray($userData) 
    { 
     $this->fromArray($userData); 
     $this->save(); 
    } 
} 

Không chắc chắn những gì bạn sẽ có thử nghiệm; nhưng, ít nhất, không có phương pháp tĩnh nữa ...

+0

Cảm ơn lời đề nghị, đó là phong cách hơn bất cứ điều gì. Tôi có thể làm cho phương pháp không tĩnh trong trường hợp cụ thể này (mặc dù tôi muốn có thể sử dụng nó mà không instantiating). –

+11

Câu hỏi chắc chắn là về phương pháp tĩnh chế nhạo - nói cho tác giả biết "không sử dụng phương pháp tĩnh" không cắt mù tạt. – Lotus

10

Có bây giờ là thư viện AspectMock để giúp với điều này:

https://github.com/Codeception/AspectMock

$this->assertEquals('users', UserModel::tableName()); 
$userModel = test::double('UserModel', ['tableName' => 'my_users']); 
$this->assertEquals('my_users', UserModel::tableName()); 
$userModel->verifyInvoked('tableName'); 
+7

Thư viện này là vàng! Nhưng tôi nghĩ họ nên đặt tuyên bố từ chối trách nhiệm trên trang của họ: "Chỉ vì bạn có thể kiểm tra các chức năng toàn cầu và phương pháp tĩnh với thư viện của chúng tôi, điều này không có nghĩa là bạn nên viết mã mới theo cách này". Tôi đọc ở đâu đó rằng một bài kiểm tra xấu tốt hơn là không có bài kiểm tra nào cả, và với thư viện này, bạn có thể thêm một mạng lưới an toàn vào mã kế thừa của mình. Chỉ cần chắc chắn để viết mã mới một cách tốt hơn :) – pedromanoel

0

Một cách tiếp cận có thể là với thư viện Moka:

$modelClass = Moka::mockClass('Model_User', [ 
    'fromArray' => null, 
    'save' => null 
]); 

$modelClass::create('DATA'); 
$this->assertEquals(['DATA'], $modelClass::$moka->report('fromArray')[0]); 
$this->assertEquals(1, sizeof($modelClass::$moka->report('save'))); 
1

Tôi sẽ tạo một lớp mới trong không gian tên thử nghiệm đơn vị mở rộng Model_User và kiểm tra điều đó. Dưới đây là một ví dụ:

gốc lớp:

class Model_User extends Doctrine_Record 
{ 
    public static function create($userData) 
    { 
     $newUser = new self(); 
     $newUser->fromArray($userData); 
     $newUser->save(); 
    } 
} 

Mock Class để gọi trong thử nghiệm đơn vị (s):

use \Model_User 
class Mock_Model_User extends Model_User 
{ 
    /** \PHPUnit\Framework\TestCase */ 
    public static $test; 

    // This class inherits all the original classes functions. 
    // However, you can override the methods and use the $test property 
    // to perform some assertions. 
} 

Trong thử nghiệm đơn vị của bạn:

use Module_User; 
use PHPUnit\Framework\TestCase; 

class Model_UserTest extends TestCase 
{ 
    function testCanInitialize() 
    { 
     $userDataFixture = []; // Made an assumption user data would be an array. 
     $sut = new Mock_Model_User::create($userDataFixture); // calls the parent ::create method, so the real thing. 

     $sut::test = $this; // This is just here to show possibilities. 

     $this->assertInstanceOf(Model_User::class, $sut); 
    } 
} 
+0

Tôi sử dụng phương pháp này khi tôi không muốn bao gồm một thư viện PHP thêm để làm điều đó cho tôi. – b01

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