前情提要

首先我們先來寫一個範例用的 Controller:

class FetchProductController extends AbstractController {
    public function __invoke(Request $request, string $id) : Response
    {
        $entityManager = $this->getDoctrine()->getManager();
        $productRepository = $entityManager->getRepository(Product::class);
        $product = $productRepository->find($id);

        // ... handle product here

        return new Response();
    }
}

這時候就會發現 $entityManager 來自 Controller 內部,但是如果我們想要 Mock ProductRepository 的話, 我們必須先 Mock EntityManager。

解法

這裡是一個測試的一小部份。

class FetchProductControllerTest extends KernelTestCase
{
    public function testFetchProduct()
    {
        $kernelInstance = static::bootKernel();

        $product = new Product();
        $product->setName("Product Name");
        // set product data

        /**
         * Mock ProductRepository
         */
        $productRepository = $this->getMockBuilder(ProductRepository::class)
            ->disableOriginalConstructor()
            ->getMock();

        $productRepository
            ->expects($this->any())
            ->method("find")
            ->will($this->returnValue($product));

        /**
         * Mock EntityManager
         */
        $entityManager = $this->getMockBuilder(EntityManager::class)
            ->disableOriginalConstructor()
            ->getMock();

        $entityManager
            ->expects($this->any())
            ->method("getRepository")
            ->will($this->returnValueMap(array(
                array(Product::class, $productRepository)
            )));

        /**
         * Set default entity manager
         */
        $kernelInstance
            ->getContainer()
            ->set("doctrine.orm.default_entity_manager", $entityManager);

        // Test Controller
    }
}

其中最重要的就是這行

$kernelInstance
    ->getContainer()
    ->set("doctrine.orm.default_entity_manager", $entityManager);

這是因為 Symfony 中的 Service 在沒有特別設定的狀態下,都會以 singleton 的方式存在, 所以不管在哪裡跟 kernel 要求 EntityManager 物件,都會拿到同一個, 因此在這裡直接將 Default entity manager 設定成我們自己 mock 出來的 EntityManager, 這樣就可以讓在 Controller 裡面的 $this->getDoctrine()->getManager() 的回傳值換成我們的 EntityManager。 `