2017-02-09 35 views
0

Symfony的2.8.13 /學說ORM 2.5.5/5.7.5的PHPUnit的Symfony2:學說:PHPUnit的:在單位嘲笑實體管理器在沖洗時設置實體ID測試

我想測試一個類的方法利用了學說實體經理。這個公共方法調用一個私有實例化一個Bookmark實體,刷新它並返回這個實體。然後,在測試的方法中,我需要訪問實體Id。除了書籤實體本身以外,一切都是嘲弄的。主要的問題是我的實體中沒有setId()方法。這裏是代碼和我的主要想法來解決這個問題,但我不知道它是否正確?

測試的類和方法

class BookmarkManager 
{ 
    //... 

    public function __construct(TokenStorageInterface $tokenStorage, ObjectManager $em, Session $session) 
    { 
     //... 
    } 

    public function manage($bookmarkAction, $bookmarkId, $bookmarkEntity, $bookmarkEntityId) 
    { 
     //... 
     $bookmark = $this->add($bookmarkEntity, $bookmarkEntityId); 
     //... 
     $bookmarkId = $bookmark->getId(); 
     //... 
    } 

    private function add($entity, $entityId) 
    { 
     //... 
     $bookmark = new Bookmark(); 
     //... 
     $this->em->persist($bookmark); 
     $this->em->flush(); 

     return $bookmark; 
    } 
} 

測試

class BookmarkManagerTest extends \PHPUnit_Framework_TestCase 
{ 
    public function testThatRestaurantAdditionToBookmarksIsWellManaged() 
    { 
     //... 
     // THIS WON'T WORK AS NO setId() METHOD EXISTS 
     $entityManagerMock->expects($this->once()) 
      ->method('persist') 
      ->will($this->returnCallback(function ($bookmark) { 
       if ($bookmark instanceof Bookmark) { 
        $bookmark->setId(1); 
       } 
      })); 
     //... 
     $bookManager = new BookmarkManager($tokenStorageMock, $entityManagerMock, $sessionMock); 
     //... 
    } 
} 

解決方案?反射類

1-製作使用所提出here

$entityManagerMock->expects($this->once()) 
    ->method('persist') 
    ->will($this->returnCallback(function ($bookmark) { 
     if ($bookmark instanceof Bookmark) { 
      $class = new \ReflectionClass($bookmark); 
      $property = $class->getProperty('id'); 
      $property->setAccessible(true); 
      $property->setValue($bookmark, 1); 
      //$bookmark->setId(1); 
     } 
    })); 

2-創建從真實一個延伸的測試Boookmark實體並添加SETID()方法。然後創建這個類的模擬,並用這個替換和定製從ReturnCallback方法得到的一個?這似乎蹩腳...

有什麼想法?謝謝你的幫助。

+0

我知道反射應該是邪惡的,但我認爲你提到的反射方法是最好的選擇。對於創建僅用於測試的類而言,感覺不對,例如TestBookmark或MockEntityManager。如果你打算在測試中定期做這些事情,我發現創建一個只有'setProperty($ object,$ property,$ value)'方法的ReflectionSetterTrait纔有用。 – mickadoo

+0

@mickadoo我同意,這個特質是一個不錯的選擇。對於實體經理我不創建一個特定的類,我只是用PHPUnit模擬構建器嘲笑它。無論如何,反思似乎是這種情況下的解決方案。 – Cruz

回答

1

反射看起來很有趣,但它降低了測試的可讀性(與嘲諷混淆使得情況變得困難)。

我會給實體管理器,並實現了基於反射有設置ID假:

class MyEntityManager implements ObjectManager 
{ 
    private $primaryIdForPersitingObject; 

    public function __construct($primaryIdForPersitingObject) 
    { 
     $this->primaryIdForPersitingObject = $primaryIdForPersitingObject; 
    } 

    ... 

    public function persist($object) 
    { 
     $reflectionClass = new ReflectionClass(get_class($object)); 
     $idProperty = $reflectionClass->getProperty('id'); 
     $idProperty->setAccessible(true); 
     $idProperty->setValue($object, $this->primaryIdForPersitingObject); 
    } 

    public function flush() { } 

    ... 
} 

一旦實現這一點,你可以注入的MyEntityManager實例,讓你的測試小,更易於維護。

你的測試看起來像

<?php 

class BookmarkManagerTest extends \PHPUnit_Framework_TestCase 
{ 
    public function testThatRestaurantAdditionToBookmarksIsWellManaged() 
    { 
     // ... 
     $entityManager = MyEntityManager(1); 
     //... 
     $bookManager = new BookmarkManager($tokenStorageMock, $entityManager, $sessionMock); 
     //... 
    } 
} 

當然,這種情況可能會很難,如果有需要許多保持對象設置不同的ID。然後你就可以,例如,增加$primaryIdForPersitingObjectpersist呼叫

public function persist($object) 
{ 
    $reflectionClass = new ReflectionClass(get_class($object)); 
    $idProperty = $reflectionClass->getProperty('id'); 
    $idProperty->setAccessible(true); 
    $idProperty->setValue($object, $this->primaryIdForPersitingObject); 

    $this->primaryIdForPersitingObject++; 
} 

它甚至可進一步擴展到具有獨立primaryIdForPersitingObject每個實體類,你的測試將是還算乾淨。

+0

感謝您的建議。這也是使用反射的好方法。這裏的技巧是正確管理不同實體的ID。也許傳遞給實體管理器構造函數的鍵/值(實體/ ID)的數組可以簡化這一點。 – Cruz

+0

@Cruz是的,它應該被構建來回應需求。這個實現是最簡單的一個,這個概念,草案,並且擴展它並不是一件難事。 –