LoginSignup
4
1

More than 5 years have passed since last update.

Symfony4のRepositoryをCodeceptionのDoctrine2モジュールを使ってUnitテストする

Posted at

Symfony4のRepositoryのメソッドに対するテストで、公式の方法ではなく、Codeceptionを使ってテストする方法をご紹介します。

Codeceptionとは

CodeceptionはPHPのテスティングフレームワークです。以下のようなテストを一括で管理、実行できます。

  • Unitテスト
  • 機能テスト
  • フレームワークでの動作テスト
  • ブラウザでのテスト
  • APIのテスト

これらのテストを一元管理できるのが好きで、このCodeceptionをよく使っています。
詳しくはこちらのスライドをご覧ください。

サンプルプログラム

テストするためのサンプルプログラムを作っていきます。今回商品テーブルのデータを扱うProductRepository,Productエンティティを用意しました。Productエンティティは

name: 商品名
price: 価格
isPublished: 公開・非公開
createdAt: 登録日時
updatedAt : 更新日時

として、Repositoryに

src/Repository/ProductRepository.php
<?php

namespace App\Repository;

use App\Entity\Product;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Symfony\Bridge\Doctrine\RegistryInterface;

/**
 * @method Product|null find($id, $lockMode = null, $lockVersion = null)
 * @method Product|null findOneBy(array $criteria, array $orderBy = null)
 * @method Product[]    findAll()
 * @method Product[]    findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
 */
class ProductRepository extends ServiceEntityRepository
{
    public function __construct(RegistryInterface $registry)
    {
        parent::__construct($registry, Product::class);
    }

    /**
     * 公開フラグで検索
     * 
     * @param $isPublished
     * @return mixed
     */
    public function findByIsPublished($isPublished)
    {
        return $this->createQueryBuilder('p')
            ->where('p.isPublished = :isPublished')
            ->setParameter(':isPublished', $isPublished)
            ->orderBy('p.id', 'ASC')
            ->getQuery()
            ->getResult();
    }
}

公開・非公開のフラグの値で検索するメソッドを作成しました。このメソッドをCodeceptionでUnitテストしてみます。
(データベース・テーブル作成については割愛します。)

Codeceptionの準備

インストール

まずはインストールです。composerでインストールします。

cd your-project
composer require codeception/codeception
# わーっとインストールが進んだ後
Symfony operations: 2 recipes (c93d57139f97ec473429f36a0ba32009)
  - Configuring phpunit/phpunit (>=4.7): From github.com/symfony/recipes:master
  -  WARNING  codeception/codeception (>=2.3): From github.com/symfony/recipes-contrib:master
    The recipe for this package comes from the "contrib" repository, which is open to community contributions.
    Review the recipe at https://github.com/symfony/recipes-contrib/tree/master/codeception/codeception/2.3

    Do you want to execute this recipe?
    [y] Yes
    [n] No
    [a] Yes for all packages, only for the current installation session
    [p] Yes permanently, never ask again for this project
    (defaults to n): y # ここでyを選択

設定 

unit.suite.ymlファイルを編集します。

tests/unit.suite.yml
# Codeception Test Suite Configuration
#
# Suite for unit or integration tests.

actor: UnitTester
modules:
    enabled:
        - Asserts
        - \App\Tests\Helper\Unit
        - Symfony:
              app_path: 'src'
              environment: 'dev'
        - Doctrine2:
              depends: Symfony
        - Db:
              dsn: "sqlite:var/data.db"
              user: ""
              password: ""
              populate: false

SymfonyDoctrine2Dbモジュールを追加します。今回はSQLiteを使いましたが、MySQLの場合はdsnとuser, passwordを設定します。編集後、このモジュールを利用できるように以下のコマンドを実行します。

vendor/bin/codecept build

これで、Codeceptionの準備は完了です。

テストを作る

まず、以下のコマンドでUnitTestのファイルを作成します。

vendor/bin/codecept g:test unit Repository/ProductRepository

これで、tests/unit/Repository/ProductRepositoryTest.phpが作成されるので、編集していきます。

tests/unit/Repository/ProductRepositoryTest.php
<?php namespace App\Tests\Repository;

use App\Entity\Product;
use Codeception\Module\Doctrine2;

class ProductRepositoryTest extends \Codeception\Test\Unit
{
    /**
     * @var \App\Tests\UnitTester
     */
    protected $tester;

    protected function _before()
    {
    }

    protected function _after()
    {
    }

    // tests

    /**
     * @throws \Codeception\Exception\ModuleException
     */
    public function testSomeFeature()
    {
        $this->createTestData();

        /** @var Doctrine2 $module */
        $module = $this->getModule('Doctrine2');
        $repository = $module->em->getRepository(Product::class);

        $rows = $repository->findByIsPublished(true);
        $this->assertCount(2, $rows);

        $rows = $repository->findByIsPublished(false);
        $this->assertCount(1, $rows);

    }

    /**
     * @throws \Codeception\Exception\ModuleException
     */
    protected function createTestData()
    {
        /** @var Doctrine2 $module */
        $module = $this->getModule('Doctrine2');
        $now = new \DateTime();
        $module->haveInRepository(Product::class, [
            'name' => '商品1',
            'price' => 100,
            'isPublished' => true,
            'createdAt' => $now,
            'updatedAt' => $now,
        ]);
        $module->haveInRepository(Product::class, [
            'name' => '商品2',
            'price' => 200,
            'isPublished' => false,
            'createdAt' => $now,
            'updatedAt' => $now,
        ]);
        $module->haveInRepository(Product::class, [
            'name' => '商品3',
            'price' => 300,
            'isPublished' => true,
            'createdAt' => $now,
            'updatedAt' => $now,
        ]);
    }
}

$this->getModule('Doctrine2'); でDoctrine2モジュールを取得します。
$module->haveInRepository('クラス名', 'データ配列'); で、テストデータを作成します。なお、毎テスト時にトランザクションを開始し、テスト後にロールバックしているのでここで作成したテストデータは保存されることはありません。

$module->em は、EntityManagerです。これを利用してProductRepositoryインスタンスを取得します。あとは、インスタンスからメソッドを実行し、想定されたデータが取得できているかテストします。

備考

ほんとうは、 haveInRepositoryなどのDoctrine2のメソッドは $this->tester->haveInRepository(); などのように$this->testerから実行できるたんですが、Codeception, Symfonyのバージョンば上がったためか、実行できなくなってました。このあたり、設定が悪いのかもしれないので、設定方法などご存知の方は教えていただきたいです。🙏

まとめ

今回はデータ取得系のテストでしたが、Doctrine2モジュールにはこの他にもデータが存在するかテストしたり、Repositoryのスタブを作ったりでき、これを機能テストやブラウザテストでも利用することができます。ぜひ一度おためしください!!

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1