2015-02-10 70 views
2

我想爲我的應用程序做一些單元測試。這是我第一次使用PHPUnit進行管理(並且更通用一些單元測試),我希望得到一個建議。UnitTest and Database

首先,讓我們說,我有這個類

class LodgingManager 
{ 
    private $session; 
    private $entity_manager; 

     public function __construct(Session $session, EntityManager $em) 
     { 
      $this->session = $session; 
      $this->entity_manager = $em; 
     } 

     public function loadLodgingList() 
     { 
      //If I've already fetched lodgings from db, use the session one. 
      //Everytime a lodging is added, session is refreshed 
      $lodging_list = $this->session->get('lodging_list'); 
      if (!$lodging_list) { 
       $lodging_repo = $this->entity_manager->getRepository('KoobiBookingEngineBundle:Lodging'); 
       $lodging_list = $lodging_repo->getAllForList(); 

       $this->session->set('lodging_list', $lodging_list); 
      } 

      return $lodging_list; 
     } 
     [...] 
    } 

這是一個簡單的和愚蠢的方法,但是,從這裏開始我顯得相當有用的代碼是不是太多複雜的追隨。正如每個人都可以看到的,我使用了一種自定義的DQL方法,可以幫助我檢索一些實體並將它們放入會話中。

我用一些虛擬數據創建了一個測試數據庫,我會測試它們。

據我所知Doctrine的EntityManager有一個受保護的__construct()方法。這意味着你不能使用「真正的」EntityManager,但你需要使用一個模擬的。 Repository等也可以做同樣的考慮。

所以我已經開發生產出的代碼是測試以下

class LodgingManagerTest extends \PHPUnit_Framework_TestCase 
{ 
    const LODGING_CLASS = 'KoobiBookingEngineBundle:Lodging'; 

    /** @var LodgingManager */ 
    protected $lodging_manager; 
    /** @var \PHPUnit_Framework_MockObject_MockObject */ 
    protected $em; 
    /** @var \PHPUnit_Framework_MockObject_MockObject */ 
    protected $repository; 
    /** @var \PHPUnit_Framework_MockObject_MockObject */ 
    protected $session; 

    public function setUp() 
    { 
     $lodging_array_collection = new ArrayCollection(); 
     $lodging = $this->getMock('Koobi\BookingEngineBundle\Entity\Lodging'); 
     $lodging_array_collection->add($lodging); 

     $repository = $this->getMockBuilder('Koobi\BookingEngineBundle\Repository\LodgingRepository') 
      ->disableOriginalconstructor() 
      ->getMock(); 
     $repository->expects($this->once()) 
      ->method('getAllForList') 
      ->will($this->returnValue($lodging_array_collection)); 
     $this->repository = $repository; 

     $em = $this->getMockBuilder('Doctrine\ORM\EntityManager') 
      ->disableOriginalconstructor() 
      ->getMock(); 
     $em->expects($this->once()) 
      ->method('getRepository') 
      ->with($this->equalTo(static::LODGING_CLASS)) 
      ->will($this->returnValue($repository)); 
     $this->em = $em; 

     $this->session = new Session(new MockArraySessionStorage()); 

     $this->lodging_manager = $this->createLodgingManager($this->session, $this->em); 
    } 

    public function testLoadLodgingList() 
    { 
     $lodging_list = $this->lodging_manager->loadLodgingList(); 
     $this->assertCount(1, $lodging_list); 

    } 
} 

每個對象是一個嘲弄一個(除了住宿是一個「真正的」一節)。但對我來說,這個測試是無用的,因爲我無法從數據庫加載實體。

我完全知道這種測試是愚蠢的,但我很容易想象其他一些複雜的需要真實對象。

因此,詢問專家用戶,您對此有何看法?我如何着手進行更相關和適當的測試?

+1

,而不是一個單元測試,在一個孤立的背景下工作,你應該做一個功能測試,通過固定裝置加載的數據,但是你必須自己建立這個賽程,所以是的,在特定情況下,您應該對存儲庫方法(或您的服務)進行功能測試。我的兩美分 – Matteo 2015-02-10 16:23:17

+0

@Matteo:謝謝你的回答。我一定會做功能測試,但據我所知,如果你想要100%(或至少如果你嘗試)覆蓋的情況下,你也應該進行單元測試。我是我錯了?此外,對於你來說,可能是一個很好的情況,可能直接測試存儲庫方法,而對經理使用「虛擬」方法?在這種情況下,我可以爲這種方法覆蓋100%的情況。 – DonCallisto 2015-02-10 16:26:26

+0

有時與夾具一起工作是一件頭痛的事情:您必須維護它們,並且您必須建立關係等等。通常,當我有複雜的業務邏輯時,我會進行單元測試,我使用控制器進行了一組功能測試(僅測試響應代碼,而不是基於DOM頁面元素),當我對自定義回購沒有真正的信心時方法我也做了功能測試。這只是我的看法,我是一個非常喜歡的TDD,但是......等待一位非常專業的人的回答!對不起,我很長的評論, – Matteo 2015-02-10 16:32:46

回答

2

你應該重構你的代碼來將'簡單,愚蠢'的dao與其他任何東西(會話管理和業務邏輯)分開。然後將你的測試分成2或3組。

  1. 單元測試:與環境(也來自數據庫)隔離測試組件。在這個考驗你嘲笑你的數據庫(EntityManager的,資料庫,但是這就是所謂的在你的技術),你檢查,如果那些嘲笑被稱爲預期

  2. 數據庫測試:你用你的真正的EntityManager,倉庫和mysql/oracle /無論測試您的sql/orm代碼是否正常工作。您正在測試的數據庫代碼沒有任何業務邏輯或會話管理

  3. 端到端測試。啓動整個應用程序和模擬用戶點擊網頁界面

1

恕我直言,你應該做的:

  1. 單元測試您的支票服務的「商業邏輯」做得好,的流代碼是正確的,等等......
  2. 功能測試與數據庫夾具一些組件(自定義庫法或特定 服務),你做(的副作用是, 你必須保持他們,你必須建立的關係等)
  3. 功能測試您的控制器只有最小的檢查(HTTP響應代碼)可能不檢查頁面的DOM元素。

希望這有助於