私はテストの世界に非常に慣れていないので、正しい方向に進んでいることを確認したいと思います。
phpunitを使用してsymfony2プロジェクトでユニットテストをセットアップしようとしています。
PHPUnitが機能しており、シンプルなデフォルトのコントローラーテストが正常に機能しています。 (ただし、これは機能テストではなく、アプリケーションの単体テストです。)
私のプロジェクトはデータベースの相互作用に大きく依存していますが、 phpunitのドキュメント から理解できる限り、\PHPUnit_Extensions_Database_TestCase
に基づいてクラスを設定し、dbのフィクスチャを作成してそこから作業する必要があります。
しかし、symfony2は、\PHPUnit_Framework_TestCase
から拡張されたWebTestCase
クラスのみを提供します。
それで、私は自分のDataBaseTestCase
を作成する必要があると思いますか。これは主にWebTestCase
をコピーします。違いは、それが\PHPUnit_Extensions_Database_TestCase
から拡張され、そのすべての抽象メソッドを実装することだけです。
または、データベース中心のテストに関するsymfony2の別の「組み込み」推奨ワークフローはありますか?
モデルが適切なデータを保存および取得することを確認したいので、誤ってdoctrineの詳細をテストしてしまいたくありません。
tl; dr:
単体テストは、サービスでは意味があり、リポジトリでは意味がありません。そして、それらのサービスはエンティティーマネージャーのモックを使用できます。 (私は言うまでもします:可能であれば、エンティティーのみがそれらに渡されることを期待するサービスを記述します。次に、それらのエンティティーのモックを作成するだけでよく、ビジネスロジックの単体テストは非常に簡単になります。)
アプリケーションの実際の使用例は、symfony2のドキュメント データベースと対話するコードをテストする方法 にかなり反映されていました。
彼らはサービステストのためにこの例を提供します:
サービスクラス:
use Doctrine\Common\Persistence\ObjectManager;
class SalaryCalculator
{
private $entityManager;
public function __construct(ObjectManager $entityManager)
{
$this->entityManager = $entityManager;
}
public function calculateTotalSalary($id)
{
$employeeRepository = $this->entityManager
->getRepository('AppBundle:Employee');
$employee = $employeeRepository->find($id);
return $employee->getSalary() + $employee->getBonus();
}
}
サービステストクラス:
namespace Tests\AppBundle\Salary;
use AppBundle\Salary\SalaryCalculator;
use AppBundle\Entity\Employee;
use Doctrine\ORM\EntityRepository;
use Doctrine\Common\Persistence\ObjectManager;
class SalaryCalculatorTest extends \PHPUnit_Framework_TestCase
{
public function testCalculateTotalSalary()
{
// First, mock the object to be used in the test
$employee = $this->getMock(Employee::class);
$employee->expects($this->once())
->method('getSalary')
->will($this->returnValue(1000));
$employee->expects($this->once())
->method('getBonus')
->will($this->returnValue(1100));
// Now, mock the repository so it returns the mock of the employee
$employeeRepository = $this
->getMockBuilder(EntityRepository::class)
->disableOriginalConstructor()
->getMock();
$employeeRepository->expects($this->once())
->method('find')
->will($this->returnValue($employee));
// Last, mock the EntityManager to return the mock of the repository
$entityManager = $this
->getMockBuilder(ObjectManager::class)
->disableOriginalConstructor()
->getMock();
$entityManager->expects($this->once())
->method('getRepository')
->will($this->returnValue($employeeRepository));
$salaryCalculator = new SalaryCalculator($entityManager);
$this->assertEquals(2100, $salaryCalculator->calculateTotalSalary(1));
}
}
これらの種類のテストに必要なテストデータベースはありません。
永続層ではなく、ビジネスロジックをテストすることが重要です。
機能テストの場合のみ、後で構築して破棄する独自のテストデータベースを用意することは理にかなっており、大きな問題は次のとおりです。
機能テストはいつ意味がありますか?
以前はすべてのものをテストするが正しい答えだと思っていました。まだそれ自体がほとんどテスト駆動ではなかった多くのレガシーソフトウェアを使用した後、私はもう少しなりました 怠惰な実用的であり、バグによって別の方法で証明されるまで、特定の機能が機能していると見なします。
XMLを解析し、XMLからオブジェクトを作成し、それらのオブジェクトをデータベースに格納するアプリケーションがあるとします。オブジェクトをデータベースに格納するロジックが機能することがわかっている場合(たとえば、会社はデータを必要とし、現時点では壊れていません)、そのロジックがひどい醜い山積みであったとしても、 imminentはそれをテストする必要があります。すべてのように、私のXMLパーサーが正しいデータを抽出することを確認する必要があります。適切なデータが保存されることを経験から推測できます。
機能テストが非常に重要なシナリオがあります。つまり、オンラインショップを作成する場合などです。購入したアイテムをデータベースに保存することはビジネスに不可欠であり、ここではテストデータベース全体を使用した機能テストが絶対的な意味を持ちます。
PHPUnit_Extensions_Database_TestCase
を使用したことはありませんが、主に次の2つの理由によります。
理論上の私のやり方...
doctrine/doctrine-fixtures-bundle をフィクスチャに使用し(どのような目的でも)、データベース全体をすべてのフィクスチャでセットアップします。次に、このデータベースに対してすべてのテストを実行し、テストによってデータベースが変更された場合は必ずデータベースを再作成します。
利点は、テストが読み取るだけで何も変更しない場合、データベースを再度セットアップする必要がないことです。変更の場合は、それをドロップして再度作成するか、変更を元に戻す必要があります。
データベースをセットアップしてからsqliteファイルをコピーし、クリーンなファイルに置き換えて元のデータベースに戻すことができるため、テストにはsqliteを使用しています。そうすれば、データベースを削除して作成し、すべてのフィクスチャを再度ロードしてクリーンなデータベースにする必要がなくなります。
...そしてコード内
symfony2とphpunitを使用してデータベーステストを行う方法に関する記事 を作成しました。
Sqliteを使用していますが、MySQLやPostgresなどを使用するように簡単に変更を加えることができると思います。
さらに考える
うまくいくかもしれない他のいくつかのアイデアはここにあります:
このクラスを使用できます。
<?php
namespace Project\Bundle\Tests;
require_once dirname(__DIR__).'/../../../app/AppKernel.php';
use Doctrine\ORM\Tools\SchemaTool;
abstract class TestCase extends \PHPUnit_Framework_TestCase
{
/**
* @var Symfony\Component\HttpKernel\AppKernel
*/
protected $kernel;
/**
* @var Doctrine\ORM\EntityManager
*/
protected $entityManager;
/**
* @var Symfony\Component\DependencyInjection\Container
*/
protected $container;
public function setUp()
{
// Boot the AppKernel in the test environment and with the debug.
$this->kernel = new \AppKernel('test', true);
$this->kernel->boot();
// Store the container and the entity manager in test case properties
$this->container = $this->kernel->getContainer();
$this->entityManager = $this->container->get('doctrine')->getEntityManager();
// Build the schema for sqlite
$this->generateSchema();
parent::setUp();
}
public function tearDown()
{
// Shutdown the kernel.
$this->kernel->shutdown();
parent::tearDown();
}
protected function generateSchema()
{
// Get the metadatas of the application to create the schema.
$metadatas = $this->getMetadatas();
if ( ! empty($metadatas)) {
// Create SchemaTool
$tool = new SchemaTool($this->entityManager);
$tool->createSchema($metadatas);
} else {
throw new Doctrine\DBAL\Schema\SchemaException('No Metadata Classes to process.');
}
}
/**
* Overwrite this method to get specific metadatas.
*
* @return Array
*/
protected function getMetadatas()
{
return $this->entityManager->getMetadataFactory()->getAllMetadata();
}
}
その後、エンティティをテストできます。このようなもの(エンティティUserがあると仮定)
//Entity Test
class EntityTest extends TestCase {
protected $user;
public function setUp()
{
parent::setUp();
$this->user = new User();
$this->user->setUsername('username');
$this->user->setPassword('p4ssw0rd');
$this->entityManager->persist($this->user);
$this->entityManager->flush();
}
public function testUser(){
$this->assertEquals($this->user->getUserName(), "username");
...
}
}
この助けを願っています。
ソース:theodo.fr/blog/2011/09/symfony2-unit-database-tests