让我们说有这样的功能:
function a()
{
$entity = $this->getEntity();
$entity->setSomePrivateVar();
$service = $this->getService();
$service->doSomething($entity);
}我想测试一下
$service->doSomething($entity);是用正确的$entity调用的。
$entity调用setSomePrivateVar()
在实际应用程序代码中,我执行了如下操作:
获取实体的模拟并测试是否调用了setSomePrivateVar。
获取$service的模拟,并测试使用参数$entity调用doSomething()。
看上去没问题。
但问题是--如果我重构代码并首先在服务上调用doSomething(),然后在$entity上调用setSomePrivateVar(),那么测试仍然可以通过。
但是这个函数现在错了,因为doSomething依赖于由setSomePrivateVar()设置的$entity私有字段。
例如,我将对此进行重构:
function a()
{
$entity = $this->getEntity();
$service = $this->getService();
$service->doSomething($entity);
// this line moved
$entity->setSomePrivateVar();
}因此,看起来PhpUnit没有检查$entity私有字段。如果是例如数组,那么with()函数将看到传递的数组与预期的不一样。
那么,如何测试doSomething()是否使$entity处于正确的状态(在将setSomePrivateVar()传递给doSomething()之前对实体进行了调用)?
也许$entity被嘲笑是有什么用的。
用真实世界的例子进行更新
public function setNotifyUsers(AnnualConsolidation $consolidation, $status)
{
$consolidation->setNotifyUsers($status); // if move this method after the flush(), tesst does not fail
$this->entityManager->persist($consolidation);
$this->entityManager->flush();
}
public function testNotifyUsers()
{
$consolidation = $this->getMockBuilder(AnnualConsolidation::class)
->setMethods(['setNotifyUsers'])
->getMock();
$consolidation
->expects($this->once())
->method('setNotifyUsers')
;
$this->entityManager
->expects($this->at(0))
->method('persist')
->with($consolidation)
;
$this->entityManager
->expects($this->at(1))
->method('flush')
;
/** @var AnnualConsolidation $consolidation */
$this->consolidationsService->setNotifyUsers($consolidation, true);
}我们正在讨论用这种方式测试setNotifyUsers方法是否有效。我试着试着不去访问数据库就进行测试。一个人认为这可能需要用命中数据库进行测试,因为如果不改变逻辑的重构方法,则可能需要进行测试来重构。另一方面,这种方法不太可能被重构那么多。
但是,也可能有一种方法,只需要测试()之后调用flush(),而不告诉索引,因为在其他示例中,在持久化之前添加了一些调用之后,可能需要更新索引,因此可能需要做太多的工作才能使测试工作正常。
,但是对于这个主题--首先,我想知道如何使测试失败--如果在setNotifyUsers ()之后移动,那么测试不会失败。如果我们使用命中数据库进行测试,我们将看到$consolidation状态没有更新。
一个人告诉我们要检查,断言传递给持久化方法的是什么。我还没有尝试,但我不确定在模拟的$consolidation上这是否是可能的。被嘲弄的$consolidation是否像真实的$consolidation那样有某种状态?
发布于 2017-03-30 21:21:52
正如你在问题中所说的
一个人告诉我们要检查,断言传递给持久化方法的是什么。
这是可行的方法,但是您的代码会使这非常困难,我认为您应该稍微重构一下,以使代码可测试。
首先,您的方法名为" setNotifyUsers“,但它实际上执行了两个操作,它在合并对象上调用了setNotifyUsers,并保存/保存了这些数据。在我看来,这是两种不同的行动,应该属于两种不同的方法。如果你写成这样的话,它可以帮助你的测试:
public function setNotifyUsers(AnnualConsolidation $consolidation, $status) {
$consolidation->setNotifyUsers($status);
}
public function persistConsolidation(AnnualConsolidation $consolidation) {
$this->entityManager->persist($consolidation);
$this->entityManager->flush();
}您可以单独测试setNotifyUser和persistConsolidation,并为调用这些函数的部分(使用consolidationsService的方法)编写一个函数测试,然后您可以使用at()功能来查看是否以正确的顺序调用这些函数。
但是:第二,您将这两种状态作为对此函数的合并,并将它们相加在一起。我不认为这样的东西属于服务,而是在调用该服务的方法中。移动该功能将再次给您带来麻烦,因为您无法测试调用它们的顺序。
但是您不需要使用mockBuilder来进行双重测试。与使用$this->getMockBuilder不同,您还可以创建一个实际为您保存数据的FakeConsolidation
然后,您还需要一个AnnualConsolidation的模拟,因为您希望能够检查该值是否被正确设置。
class FakeConsolidation extends AnnualConsolidation {
protected $id;
proteced $status;
public function getId() {
return $this->id;
}
public function setId($id) {
$this->id = $id;
}
public function setNotifyUsers($status) {
$this->status = $status;
}
public function shouldNotifyUsers() {
$this->status
}
}现在,因为您将为具有状态的持久化提供一个对象,所以我们可以在"with“部分中检查该状态。
当然,我不知道您的代码是如何构造的,所以我做了一些假设,只要在需要的地方进行调整,并使用您所拥有的接口。
就像这样,您甚至可以在这个问题中展示代码时测试它:
class SomethingTest extends PHPUnit_Framework_TestCase {
private $consolidationsService;
private $entityManager;
/**
* {@inheritdoc}
*/
public function setUp() {
$this->entityManager = $this->getMockBuilder(EntityManager::class)->getMock();
$this->consolidationsService = new ConsolidationsService($this->entityManager);
}
public function testNotifyUsers() {
$consolidation = new FakeConsolidation();
$consolidation->setId(1);
$this->entityManager
->expects($this->at(0))
->method('persist')
->with($this->callback(
function($savedConsolidation) {
return $savedConsolidation->shouldNotifyUsers() === true;
}
));
$this->entityManager
->expects($this->at(1))
->method('flush');
/** @var AnnualConsolidation $consolidation */
$this->consolidationsService->setNotifyUsers($consolidation, TRUE);
}
}现在,当您将setNotifyUsers移动到持久化下面时
with($this->callback(
function($savedConsolidation) {
return $savedConsolidation->shouldNotifyUsers() === true;
}
)); 您的测试将失败,因为状态尚未设置。
https://stackoverflow.com/questions/42937049
复制相似问题