Lets say there is such function:
function a()
{
$entity = $this->getEntity();
$entity->setSomePrivateVar();
$service = $this->getService();
$service->doSomething($entity);
}
I want to test that
$service->doSomething($entity);
is called with correct $entity.
The $entity calls setSomePrivateVar()
In real application code I have done something like this:
Get mock of entity and test that setSomePrivateVar is called.
Get mock of $service and test that doSomething() is called with parameter $entity.
Looks ok.
But the problem is - if I refactor code and first call doSomething() on service and then setSomePrivateVar() on $entity, test still passes.
But the function is now wrong, because doSomething is depending on $entity private field which is set by setSomePrivateVar().
For example I would refactor to this:
function a()
{
$entity = $this->getEntity();
$service = $this->getService();
$service->doSomething($entity);
// this line moved
$entity->setSomePrivateVar();
}
So it looks like PhpUnit is not checking $entity private fields. If it was for example array, then with() function would see that array passed is not same as expected.
So how do I test that doSomething() gets $entity in correct state (that setSomePrivateVar() was called on entity before passing it to doSomething() )?
Maybe it has something to do that $entity is mocked.
Update with real world example
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);
}
We were discussing if testing the setNotifyUsers method this way is even good. I was trying to test without hitting the database. One guy thinks that this might be needed to test with hitting database, because if refactoring method without changing logic, test might be needed to refactor. On the other hand - this method is not likely to be refactored that much.
But maybe also there is a way to just test that flush() is called after persist() without telling indexes, because in other examples, the indexes then might be needed to update after adding some call before persist and so might be too much work to keep tests working.
But for this topic - first I want to know how to make test fail - if I move setNotifyUsers after the flush(). Test is not failing. While if we would test with hitting database - we would see that $consolidation status is not updated.
One guy told to check, assert what is passed to the persist method. I did not try yet, but I am not sure if on mocked $consolidation this will be possible. Does mocked $consolidation has some state as real $consolidation would have?