doctrine またはその他の一般的なサービスを使用する symfony 2 サービスを単体テストする正しい方法を見つけるのに苦労しています。
私がこれまでに行ったこと:
- 私の理解では、コントローラーのアクションは次のようにする必要があります。
- できるだけ短くする
- リクエストを受け取ります
- 注入されたサービスから必要なメソッドを実行する
- これから応答を作成する
- サービスそのものです
軽量のアクションを実現するために、コントローラーに注入される別のサービスにロジックをカプセル化しようとしました。
これは、すべてをテストすることを期待してうまく機能します。
ここに私の現在のコード:
コントローラ
class SearchController
{
// search_helper, request and templating are controller-injected
protected $search_helper;
protected $request;
protected $templating;
// ...
public function searchAction()
{
$searchterm = strtolower($this->request->query->get('q'));
$result = $this->search_helper->findSamples($searchterm);
// Found a single result. Redirect to this page
if (is_string($result))
{
return new RedirectResponse($result, 301);
}
return new Response($this->templating->render('AlbiSampleBundle:Search:index.html.twig', array('results' => $result)));
}
}
検索サービス
class SearchHelper
{
// doctrine, session and min_query_len are controller-injected
protected $doctrine;
protected $session;
protected $min_query_len;
// ...
public function findSamples($searchterm)
{
if (strlen($searchterm) < $this->min_query_len)
{
$msg = 'Your search must contain at least 3 characters!';
$this->session->getFlashBag()->add('error', $msg);
return false;
}
$em = $this->doctrine->getManager();
$results = $em->getRepository('AlbiSampleBundle:Sample')->findPossibleSamples($searchterm);
// Execute a more advanced search, if std. search don't delivers a result
// ...
return $results;
}
}
このコードを正しくテストするにはどうすればよいですか?
- リポジトリは phpunit_db とインメモリ sqlite データベースでテストされています ✓</li>
- アクションは、簡単な機能テストでテストできます ✓</li>
- 残っているのは検索サービスのロジックです。例: findSamples メソッド
私が最初に考えたのは、依存関係をモックすることでした (実際、依存関係を分離する際の主要な側面の 1 つでした) が、ドクトリン オブジェクトだけでなく、エンティティ マネージャーとリポジトリもモックする必要があります。
$em = $this->doctrine->getManager();
$results = $em->getRepository('AlbiSampleBundle:Sample')->findPossibleSamples($searchterm);
もっと良い解決策があるはずだと思います。このモックには多くの LOC が必要になるだけでなく、正しくないとも感じられます。テストは、不必要に SUT に非常に緊密に結合されます。
編集
これが私が思いついたサンプルテストです。モック オブジェクトの使用。テストはうまくいきません。もっと多くのモックオブジェクトが必要になることに気づき、これは正しい方法ではないと感じました。
SessionMock->getFlashbag
メソッドでフラッシュバッグを返さないため、テストは失敗しますadd
。
doctrine->getManager
いいえを返しますEntityManager
。には方法EntityManager
がありませんgetRepository
。また、リポジトリがありませんfindPossibleSamples
。
class SearchHelperTest extends \PHPUnit_Framework_TestCase
{
private $router;
private $session;
private $doctrine;
public function setUp()
{
parent::setUp();
// ...
}
public function testSearchReturnValue()
{
$search_service = $this->createSearchHelper();
$this->assertFalse($search_service->findSamples('s'));
}
protected function createSearchHelper()
{
return new SearchHelper($this->doctrine, $this->router, $this->session, 3);
}
protected function getDoctrineMock()
{
return $this->getMock('Doctrine\Bundle\DoctrineBundle\Registry', array('getManager'), array(), '', false);
}
protected function getSessionMock()
{
return $this->getMock('Symfony\Component\HttpFoundation\Session\Session', array('getFlashBag'), array(), '', false);
}
protected function getRouterMock()
{
return $this->getMock('Symfony\Component\Routing\Router', array('generate'), array(), '', false);
}
}
よくテストされたコードを書いて、コミュニティが私を助けてくれることを願っています:)
乾杯