支付宝扫一扫付款
微信扫一扫付款
(微信为保护隐私,不显示你的昵称)
如果你的代码与数据库互动,比如,从中读取数据或向其写入数据,你需要考虑到这点来调整测试(代码)。如何处理这个问题有多种方式。在单元测试中(unit test),你可以为 Repository
创建一个mock,并使用它来返回预期的对象。在功能测试中(functional test),你可能需要准备一个“带有预置值”的数据库来确保你的测试始终拥有能够使用的“相同数据”。
若你需要直接测试查询,见 如何测试Doctrine Repositories。
如果你希望基于独立的Doctrine repository来测试代码,你需要mock Repository
。通常你要注入 EntityManager
到你的类中,并使用它来获取repository。这就令事情有些棘手因为你需要同时mock EntityManager
和你的宝库类。
把你的repository作为 factory service 注册(到容器中)再行注入之是可以的(并且非常好)。这在设置上略繁杂,但却令测试变容易,因为你只需mock那个repository即可。
假设你希望测试的类是下面这种:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | // src/AppBundle/Salary/SalaryCalculator.php
namespace AppBundle\Salary;
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();
}
} |
由于 ObjectManager
是通过构造器注入到类中的,因此在测试中,传给它一个模拟对象(a mock object)是很容易的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | // tests/AppBundle/Salary/SalaryCalculatorTest.php
namespace Tests\AppBundle\Salary;
use AppBundle\Entity\Employee;
use AppBundle\Salary\SalaryCalculator;
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->createMock(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
// 现在,模拟repository令其返回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以返回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));
}
} |
本例中,你“从内而外”地构建了mocks,先创建了由 Repository
所返回的“雇员”(employee),它本身又是通过 EntityManager
来返回。在这种方式下,并没有真实的类被郑入测试。
如果你有功能测试,你需要令其与真实数据库互动。多数情况下,你需要使用一个专门的数据库连接以确保不去覆写你在开发过程中(已经)输入的数据,同时还要在每次测试之前“能够清除数据库”。
要这样做,你可以指定一个数据库配置,去覆写默认的配置:
1 2 3 4 5 6 7 8 | # app/config/config_test.yml
doctrine:
# ...
dbal:
host: localhost
dbname: testdb
user: testdb
password: testdb |
1 2 3 4 5 6 7 8 9 | <!-- app/config/config_test.xml -->
<doctrine:config>
<doctrine:dbal
host="localhost"
dbname="testdb"
user="testdb"
password="testdb"
/>
</doctrine:config> |
确保你的数据库运行在localhost,并且定义了数据库名称和凭据等配置。
本文,包括例程代码在内,采用的是 Creative Commons BY-SA 3.0 创作共用授权。