2016-07-18 77 views
4

我正在測試一個對象,該對象旨在測試用戶是否擁有給定的電子郵件。因此,在調用「tryEmail」方法時,它會向給定的電子郵件地址發送帶有確認鏈接的消息。我的測試如下所示:PHPUnit:在一個測試中對模擬方法使用多個斷言是否是一種不好的做法?

public function testSendingWasSuccessful() { 

    $confirmationObject = $this->getMock('LT\EmailConfirmation\Model\ConfirmationObjectInterface'); 

    $testType = 'test.type'; 
    $testEmail = '[email protected]'; 
    $testData = []; 

    // EmailTester should create a new confirmation object. 
    $this->manager->expects(static::once()) 
     ->method('create')->with($testType, $testEmail) 
     ->willReturn($confirmationObject); 

    // Then it should send the confirmation message. 
    $this->mailer->expects(static::once()) 
     ->method('send')->with(static::identicalTo($confirmationObject)) 
     ->willReturn(true); 

    // And save the confirmation object. 
    $this->manager->expects(static::once()) 
     ->method('save')->with(static::identicalTo($confirmationObject)); 

    $tester = new EmailTester($this->repository, $this->manager, $this->confirmationHandler, $this->mailer); 

    static::assertTrue($tester->tryEmail($testType, $testEmail, $testData)); 
} 

現在您可以看到它可能有什麼問題 - 它包含多個斷言。爲什麼我決定在一次測試中使用這些斷言?因爲他們彼此依賴。因此,只有在創建新的確認對象時才應發送確認消息,並且只有在確認消息已發送時才應保存確認對象,最後,使用這些模擬方法的「tryEmail」方法的輸出正在斷言。

但是,我覺得我不小心只是用我的斷言描述了「tryEmail」方法的實現。但是,似乎需要對這種方法進行全面的介紹,並且確保它始終按照應有的方式工作。我可以想象如果我將刪除任何這些斷言的錯誤傳遞。例如:static::identicalTo($confirmationObject)這基本上是:check if the object passed to the mailer is the same as the one created before。如果我改變郵件的界面,我將不得不改變EmailTester的這個測試,所以看起來我在這裏做錯了什麼。然而,在同一時間 - 我怎樣才能檢查上述斷言,而不引入這種耦合?或者,也許我應該離開這未經考驗?

我在做對還是錯?我怎麼能改進它?何時真的使用嘲諷斷言?

補充:我只是有一個想法 - 是不正確的,測試類應該測試實施(如果實現與接口符合)?這意味着在測試中描述實現實際上是一件好事,因爲它確保實現正確工作。這也意味着實施的耦合水平將被轉移到測試中,並且是不可避免的。我錯了嗎?

回答

1

「每個測試一個斷言」的規則是讓您的測試集中在正在測試的代碼的一個特定行爲上。在測試中有多個斷言並不是一件壞事。

當使用模擬對象時,我更喜歡對被替換的方法進行某種斷言。這樣我確保系統將按照預期使用依賴關係。

您正在測試的課程是確認您的代碼的行爲。您擁有的斷言將是您手動進行的任何檢查,以確保該班級的行爲符合您的預期。既然您期望以特定的方式調用特定的方法,您希望對它們有一個斷言。

我在測試中看到的問題是您有一個模擬對象返回一個模擬對象。這通常是一種代碼異味,意味着你沒有傳遞正確的依賴關係。您可以將創建的LT\EmailConfirmation\Model\ConfirmationObjectInterface對象移出該方法,並將其作爲方法的依賴項傳遞。用此對象替換方法的前兩個參數。

在這個測試中,您似乎也沒有使用第三個參數,所以它似乎沒有必要。

+0

感謝您的回答!您對'LT \ EmailConfirmation \ Model \ ConfirmationObjectInterface'的評論給了我一些想法。我有意識地設計它,以儘量減少使用此對象時所需的工作量。現在我幾乎可以在控制器中使用一個函數'tryEmail'並完成。否則,我將不得不拉動管理器,並在控制器中創建確認對象,然後通過'tryEmail'方法傳遞它,這將只是我的Controller中的更多代碼,這似乎是一個壞主意。你確定這會有所改進嗎? –

+0

我很有信心。我爲你的對象增加了靈活性,'EmailTester'現在可以採用不同類型的對象。它也讓他們專注。如果您需要發送不同的電子郵件類型,則此對象應該能夠毫無問題地處理它。 – Schleis

相關問題