簡體   English   中英

如何讓我的PHPUnit測試更簡潔,更長久?

[英]How can I make my PHPUnit tests more succinct, less long?

我正在為我的Web應用程序編寫的PHPUnit測試用他們的長度和不透明性來殺死我。 似乎測試中的代碼比他們測試的代碼要多一個數量級。

例如,假設我的網站有一個CatController對象,就是這個方法:

public function addCat(Default_Model_Cat $cat)
{
    $workflow = $this->catWorkflowFactory->create(array($this->serviceExecutor));
    $workflow->addCat($cat);
}

我必須創建的單元測試才能徹底測試它是這樣的:

public function testAddCat()
{
    $cat = $this->getMockBuilder('Default_Model_Cat')
        ->disableOriginalConstructor()
        ->getMock();
    $controller = $this->getMockBuilder('CatController')
        ->disableOriginalConstructor()
        ->setMethods(array('none'))
        ->getMock();
    $workflow = $this->getMockBuilder('Default_Model_Workflow_Cat')
        ->setMethods(array('addCat'))
        ->disableOriginalConstructor()
        ->getMock();
    $workflow->expects($this->once())
        ->method('addCat')
        ->with($cat);
    $controller->serviceExecutor = $this->getMockBuilder('ServiceExecutor')
        ->disableOriginalConstructor()
        ->getMock();
    $controller->catWorkflowFactory = $this->getMockBuilder('Factory')
        ->disableOriginalConstructor()
        ->setMethods(array('create'))
        ->getMock();
    $controller->catWorkflowFactory->expects($this->once())
        ->method('create')
        ->with($controller->serviceExecutor)
        ->will($this->returnValue($workflow));
    $controller->addCat($cat);
}

是否有任何語法可以使單元測試更短更容易閱讀? 例如,而不是說“這個對象是一個模擬將調用此方法的方法,並且當調用此方法時,它將使用此參數調用一次,並將返回此值”如果我可以,那將是很好的只說once(object->method(argument)) => $returnvalue

你可以設計的類越多,你就可以在單元測試中使用它而不需要模擬,你需要編寫的代碼就越少。 但是對於上面的例子,我的第一反應是這個方法不需要單元測試,因為它不是真正執行任何邏輯,並且在寫入后不會改變。

話雖這么說,假設您需要在此類的其他方法中使用工作流實例,請將創建它的代碼提取到新方法中。 這允許您為每個測試模擬該方法,並且在一次測試中只有更長的模擬。

例如,如果您還有一個removeCat()方法,它將如下所示:

public function addCat(Default_Model_Cat $cat) {
    $this->createWorkflow()->addCat($cat);
}

public function removeCat(Default_Model_Cat $cat) {
    $this->createWorkflow()->removeCat($cat);
}

public function createWorkflow() {
    return $this->catWorkflowFactory->create(array($this->serviceExecutor));
}

上面的方法非常短,實際上不需要單元測試,但現在它們會更短一些:

public function testAddCat() {
    $cat = $this->getMockBuilder('Default_Model_Cat')
        ->disableOriginalConstructor()
        ->getMock();
    $controller = $this->getMockBuilder('CatController')
        ->disableOriginalConstructor()
        ->setMethods(array('createWorkflow'))
        ->getMock();
    $workflow = $this->getMockBuilder('Default_Model_Workflow_Cat')
        ->setMethods(array('addCat'))
        ->disableOriginalConstructor()
        ->getMock();
    $controller->expects($this->once())
        ->method('createWorkflow')
        ->will($this->returnValue($workflow));
    $workflow->expects($this->once())
        ->method('addCat')
        ->with($cat);
    $controller->addCat($cat);
}

如果控制器中有許多此類方法,則可以在測試用例中創建輔助方法來創建模擬。 最后,你真的需要禁用你的模擬器上的原始構造函數嗎? 我自己很少需要這樣做。

如果你有一個CatController對象,你不應該在測試中嘲笑它,如果可能的話。 你想測試那個類,所以使用真正的類。

你可以擺脫所有"setMethod"調用。 默認情況下, 所有方法都將被模擬,這就是您想要的。

還有其他一些Phake庫可以PhakePhakeMockery這樣的代碼行,而有些人喜歡它,但我沒有太多的個人經驗。

令我覺得有點奇怪的是你使用公共屬性設置模擬。 我原以為那些會進入Controllers構造函數。

鑒於那就是你可以做到的方法:

public function testAddCat()
{
    $cat = $this->getMockBuilder('Default_Model_Cat')
        ->disableOriginalConstructor()
        ->getMock();

    $workflow = $this->getMockBuilder('Default_Model_Workflow_Cat')
        ->disableOriginalConstructor()
        ->getMock();
    $workflow->expects($this->once())
        ->method('addCat')
        ->with($cat);

    $controller = new CatController(/*if you need params here pass them!*/);
    // You can use this to avoid mocking the object if you want
    // If your tests are more of a usage doc maybe don't
    $controller->serviceExecutor = "My fake Executor";

    $controller->catWorkflowFactory = $this->getMockBuilder('Factory')
        ->disableOriginalConstructor()
        ->getMock();
    $controller->catWorkflowFactory->expects($this->once())
        ->method('create')
        ->with(array("My fake Executor"))
        ->will($this->returnValue($workflow));

    $controller->addCat($cat);
}

讓我們把一些常見的東西放到設置中

只是為了讓每個函數都更好看一下,讓我們把默認的模擬帶入設置。

public function setUp() {

    $this->controller = new CatController(/*if you need params, pass them!*/);
    $this->serviceExecutor = $this->getMockBuilder('ServiceExecutor')
        ->disableOriginalConstructor()
        ->getMock();
    $this->controller->serviceExecutor = $this->serviceExecutor;
    $this->cat = $this->getMockBuilder('Default_Model_Cat')
        ->disableOriginalConstructor()
        ->getMock();
    $this->workflow = $this->getMockBuilder('Default_Model_Workflow_Cat')
        ->disableOriginalConstructor()
        ->getMock();
    $this->controller->catWorkflowFactory = $this->getMockBuilder('Factory')
        ->disableOriginalConstructor()
        ->getMock();
}

和方法:

public function testAddCat()
{
    $this->workflow->expects($this->once())
        ->method('addCat')
        ->with($this->cat);

    $this->controller->catWorkflowFactory->expects($this->once())
        ->method('create')
        ->with(array($this->serviceExecutor))
        ->will($this->returnValue($this->workflow));

    $this->controller->addCat($cat);
}

它仍然不是漂亮,但我們將它分成更易於管理的塊。

安裝程序會創建所有虛假對象,但它們不會執行任何操作(因此它們不會因任何測試而失敗,並且安裝時間應該是疏忽

而測試則側重於描述應該發生的事情。


一般來說,我會說“如果使用這個類很復雜,那么測試表明需要做很多事情是件好事”。 如果那是一個問題,可能會改變課程。 生產可以使用它也將很難設置一切正確。 但是很多框架/方法使控制器在這方面“特別”,所以你最了解:)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM