tl;博士:
我最初的问题中的某些方面清楚地表明,我对单元测试和功能测试之间的区别缺乏理解。(正如我所写的,我想对应用程序进行单元测试,但同时也在谈论控制器测试;这些是定义上的功能测试)。
单元测试只对服务有意义,对存储库没有意义。这些服务可以使用实体管理器的模拟。(我什至会说:如果可能的话,编写只期望将实体传递给它们的服务。然后您只需要创建这些实体的模拟,您的业务逻辑的单元测试就变得非常简单。)
我的应用程序的实际用例很好地反映在 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 解析器提取正确的数据。我可以根据经验推断将存储正确的数据。
在某些情况下,功能测试非常重要,例如,如果要编写一个在线商店。将购买的物品存储到数据库中将是业务关键,在这里使用整个测试数据库进行功能测试是绝对有意义的。