I want to use Prophecy ("phpspec/prophecy-phpunit") for the first time to create unit tests for my classes. I want to test a function that calls another function in the same service, here's the code:
class UserManager
{
private $em;
private $passwordHelper;
public function __construct(\Doctrine\ORM\EntityManager $em, \MainBundle\Helper\PasswordHelper $passwordHelper)
{
$this->em = $em;
$this->passwordHelper = $passwordHelper;
}
public function getUserForLdapLogin($ldapUser)
{
$dbUser = $this
->em
->getRepository('MainBundle:User')
->findOneBy(array('username' => $ldapUser->getUsername()));
return (!$dbUser) ?
$this->createUserFromLdap($ldapUser) :
$this->updateUserFromLdap($ldapUser, $dbUser);
}
First problem I had was that I was using findOneByUsername
and Prophecy, as far as my knowledge goes, does not allow you to: mock magic methods (_call
for EntityRepository
), mock methods that do not exist, mock the class you are testing. If these are true I'm in a bit of a pickle, meaning I cannot test this function without testing the other functions in the class.
So far, my test looks like this:
class UserManagerTest extends \Prophecy\PhpUnit\ProphecyTestCase
{
public function testGetUserForLdapLoginWithNoUser()
{
$ldapUser = new LdapUser();
$ldapUser->setUsername('username');
$em = $this->prophesize('Doctrine\ORM\EntityManager');
$passwordHelper = $this->prophesize('MainBundle\Helper\PasswordHelper');
$repository = $this->prophesize('Doctrine\ORM\EntityRepository');
$em->getRepository('MainBundle:User')->willReturn($repository);
$repository->findOneBy(array('username' => 'username'))->willReturn(null);
$em->getRepository('MainBundle:User')->shouldBeCalled();
$repository->findOneBy(array('username' => 'username'))->shouldBeCalled();
$service = $this->prophesize('MainBundle\Helper\UserManager')
->willBeConstructedWith(array($em->reveal(), $passwordHelper->reveal()));
$service->reveal();
$service->getUserForLdapLogin($ldapUser);
}
}
And of course, the tests fail because the promises on $em
, and the repository are not fulfilled. If I instantiate the class I am testing, the tests fail because the function then calls createUserFromLdap()
on the same class and that is not tested.
Any suggestions?
Regarding your problem of not being able to mock methods that do not exist, you could use
http://docs.mockery.io/en/latest/
in stead of prophecy. Mockery allows you to do just that. Strictly speaking, that does break some of the rules of good design, but on the other hand, sometimes it's just very useful. Anyways, mockery is very similar, as far as features go, and it's equally as intuitive and easy to use imo. However, they still haven't released stable version, so just be aware of that if you do decide to use it.
Here you can find a good comparison of two libraries
http://everzet.com/post/72910908762/conceptual-difference-between-mockery-and-prophecy