How to mock a Doctrine Repository in Zend Framework 3 using PHPUnit - doctrine-orm

I am trying to test a ZF3 controller action which, in the process, selects a user from the database via a Doctrine ORM repository using a token given as a GET-Parameter. As I need to make sure that the User exists I need to create a mock of the repository returning the user object. How do I do this?
My setup is the following:
The class UserControllerFactory is instantiating a UserController class:
class UserControllerFactory implements FactoryInterface {
/**
* #param ContainerInterface $container Zend\ServiceManager\ServiceManager
* #param string $requestedName
* #param array|NULL $options
*
* #return UserController
*/
public function __invoke(ContainerInterface $container, $requestedName, Array $options = NULL) {
$entityManager = $container->get('doctrine.entitymanager.orm_default');
$userRepository = $entityManager->getRepository('User\Entity\User');
return new UserController($container, $entityManager, $userRepository);
}}
In the UserController the acton resetPassword is called. It gets the needed parameter from the route and selects a user from the database matching the token:
public function resetPasswordAction() {
$request = $this->getRequest();
$passwordResetToken = $this->params()->fromRoute('token');
if(strlen(trim($passwordResetToken))) {
$user = $this->userRepository->findOneBy(
[
'passwordResetToken' => $passwordResetToken
]
);
...
If no user is found. The action will redirect to user to a different action.
PHPUnit test case:
public function testResetPasswordActionCanBeAccessed() {
$passwordResetToken = 'testToken1234';
$this->dispatch("/user/resetPassword/$passwordResetToken", 'GET');
$this->assertNotRedirect();
}
As there is no user having the token is will be redirected.
To my knowledge I need to create a mock of the repository (userRepository), create a mock user and use the mock repository retrieve the mock user having the token.
I am not sure if this is the right approche as I tried a lot of tutorial and never got it to work. I don't know how to "replace" the, in the action called "userRepository" with the in the unit test created mock object.
I am happy to provide more details if needed.
EDIT
As suggested by #DonCallisto (thank you) I changed my test case code to:
...
$mockedEm = $this->createMock(EntityManager::class);
$mockedUserRepository = $this->createMock('Core\Repository\EntityRepository');
$mockedEm->method('getRepository')->willReturn($mockedUserRepository);
$mockedUserRepository->method('findOneBy')->willReturn($mockedUser);
$this->dispatch("/$this->_lang/user/resetPassword/$passwordResetToken", 'GET');
...
but after calling the "dispatch" in the test case my controller call
$user = $this->userRepository->findOneBy(...)
will still return NULL instead of the mocked user object given in the test. If I debug the $mockedUserRepository, my $mockedUser is assigned correctly.
I also tried the suggested:
$mockedUserRepository->findOneBy([arrayWithParams])->willReturn($mockedUser);
But this will through an error due to the fact that $mockedUserRepository->findOneBy() is returning NULL.

Related

How to Mock EntityManager in a serviceTest with Symfony?

Sorry my question is very long.
How to mock entityManager in a service test extending KernelTestCase ?
Now, the explanation and my tests...
I am using Symfony3.2. My Application is standard. I have some Controller and I use WebTestCase to test them.
Generaly, my Controller verify parameters, call a service/a manager, handle some variables and push them to view and my test are pretty simple in test extending WebTestCase.
/**
* Test New Game Action
*/
public function testFooAction(){
//We mock the Service
$fooService = $this
->getMockBuilder(GameService::class)
->disableOriginalConstructor()
->getMock();
$fooService->expects(self::once())
->method('barMethod')
->willReturn($result);
//We create the client
$client = static::createClient();
$container = $client->getContainer();
//I put my mock here
$container->set('app.game-service', $fooService);
//I launch the request
$client->request('GET', '/foo');
//I handle the response
$response = $client->getResponse();
//I do some tests like this one
self::assertEquals(200, $response->getStatusCode());
}
As you can see, I do not call EntityManger, because I use Services and these lines to put my Services's Mock
//We create the client
$client = static::createClient();
$container = $client->getContainer();
//I put my mock here
$container->set('app.game-service', $fooService);
I have a problem to mock Entity Manager in my services. My Controller is tested but not My Service.
Here is the constructor initialized a simple protected property entityManager. The problem is this protected as you will see further...
/**
* FooService constructor.
* #param EntityManager $entityManager
*/
public function __construct(EntityManager $entityManager)
{
$this->entityManager = $entityManager;
}
To Test my Service, here is my initial code :
<?php
class FooServiceTest extends KernelTestCase
{
/**
* Foo Service.
*
* #var FooService
*/
private $fooService;
/**
* Prepares the environment before running each test.
*/
protected function setUp()
{
parent::setUp();
self::bootKernel();
$this->fooService = static::$kernel->getContainer()
->get('app.foo-service') //HERE IS HOW I HANDLE MY SERVICE TO TEST IT
;
}
testStart Works perfectly, there is some database interaction.
I know that I can rollback data during test. But I want to Mock entityManager to verify the call of commit method.
I try this to mock entity manager in my setUp :
protected function setUp()
{
parent::setUp();
$entityManager = $this
->getMockBuilder('Doctrine\ORM\EntityManager')
->disableOriginalConstructor()
->getMock();
$entityManager
->expects(once()) // I WANT EXACTLY ONE CALL TO COMMIT METHOD
->method('commit')
->willReturn(null);
self::bootKernel();
$container = static::$kernel->getContainer();
$container->set('doctrine.orm.entity_manager', $entityManager); // THIS LINE DOES NOTHING <=======
$this->gameService = static::$kernel->getContainer()
->get('app.game-service')
;
Thes code doesn't work. Mock is not in place. I still have the true entityManager. I think it is because the container is already closed. Do it is nut usefull to set
Like a Barbarian, I change the entityManager property to public
And I do that :
protected function setUp()
{
parent::setUp();
$entityManagerMock = $this
->getMockBuilder('Doctrine\ORM\EntityManager')
->disableOriginalConstructor()
->getMock();
$entityManagerMock
->expects(once()) // I WANT EXACTLY ONE CALL TO commit METHOD
->method('commit')
->willReturn(null);
self::bootKernel();
$this->gameService = static::$kernel->getContainer()
->get('app.game-service')
;
$this->gameService->entityManager = entityManagerMock;
It works perfectly. Test can be run. But it is NOT a good practice to have an entityManager in a public property
My question is : How to mock entityManager in a service test ?
(Sorry, I am not fluent in english)
First of all in your code
$container->set('doctrine.orm.entity_manager'); // THIS LINE DOES NOTHING <=======
I'm sure you missed second parameter :)
Another note: don't mock what you don't own.
In future you will run composer update and EntityManager's commit will have some optional isReallyCommit parameter. Your code will be broken, but you will not notice it, because tests are green. I understand that it very unlikely, but anyway it's just example. I think the good practice here is to
Have some adapter with entity manager incapsulated
Mock that adapter in service unit tests
Test your adapter with functional tests against real database without mocking anything
Or just don't unit test your service but make functional tests with real database interactions

Laravel 5 Cannot Mock View

I'm trying to write a really basic test for one of my controllers
/**
* THIS IS MY CONTROLLER. $this->badge is a repository
* #return \Illuminate\Http\Response
*/
public function index()
{
return view('badges.index')->with([
'badges' => $badges = $this->badge->all()
]);
}
I'm using repositories which return Eloquent collections. My basic test is as follows:
public function testItShowsAllBadges()
{
// Arrange
//DISABLE AUTH MIDDLEWARE ON THIS ROUTE
$this->withoutMiddleware();
// MOCK THE REPO
$this->badge->shouldReceive('all')->andReturn(new Illuminate\Support\Collection);
// Act
$response = $this->action('GET', 'BadgeController#index');
// Assert
$this->assertResponseOk();
$this->assertInstanceOf('Illuminate\Support\Collection', $response->original->getData()['badges']);
$this->assertViewHas('badges');
}
This test fails with a message 'trying to get property of non-object'. This is because I do Auth::user()->something in the view.
So I need to mock the view but I don't know how. Can someone advise?
Other SO answers do not seem to work and just result in Exceptions being thrown in the test about methods not existing on the Mock. I have tried for example:
View::shouldReceive('make')
->once()
->andReturn(\Mockery::self())
Adding this before I call the route results in a 500 error 'Method Mockery_1_Illuminate_View_Factory::with() does not exist on this mock object'. I tried adding in
->shouldReceive('with')
->once()
->andReturn(\Mockery::self());
However this results in an Exception stating that getData() does not exist on this Mock Object. Even removing that assertion, assertViewHas('badges') fails saying the response was not a view.
Also I haven't understood if View::shouldReceive... is part of Arrange or Assert phase of the test?My understanding it is part of the arrange and should go before the $this->action(....)

Understanding the logic flow of mock tests

In an effort to learn the logic flow of mock tests I've unsuccessfully reproduced a test from a Symfony article using code from my application.
Background: Volunteer entity extends the abstract class Person, which extends the FOSUserBundle model User. Person includes methods for firstName, lastName, and name. Name returns lastName, firstName. The test that appears below returns this:
--- Expected
+++ Actual
## ##
-'Borko, Benny'
+', '
How should this test be modified? Better yet, how do you tell when it's your test design and not the system under test that's failing?
Edit: see result of applying proposed solution below
Edit #2:
Not sure if this is relevant: Volunteer and two other entities are mapped via Inheritance Mapping to the Person entity (see PUGXMultiUserBundle).
The test:
use Truckee\MatchingBundle\Entity\Volunteer;
class MockVolunteerTest extends \PHPUnit_Framework_TestCase
{
public function testFullName()
{
// First, mock the object to be used in the test
$volunteer = $this->getMock('\Truckee\MatchingBundle\Entity\Volunteer');
$volunteer->expects($this->once())
->method('getFirstName')
->will($this->returnValue('Benny'));
$volunteer->expects($this->once())
->method('getLastName')
->will($this->returnValue('Borko'));
// Now, mock the repository so it returns the mock of the volunteer
$volunteerRepository = $this->getMockBuilder('\Doctrine\ORM\EntityRepository')
->disableOriginalConstructor()
->getMock();
$volunteerRepository->expects($this->once())
->method('find')
->will($this->returnValue($volunteer));
// Last, mock the EntityManager to return the mock of the repository
$em = $this->getMockBuilder('\Doctrine\Common\Persistence\ObjectManager')
->disableOriginalConstructor()
->getMock();
$em->expects($this->once())
->method('getRepository')
->will($this->returnValue($volunteerRepository));
$user = new Volunteer();
$this->assertEquals('Borko, Benny', $user->getName());
}
}
Proposed solution:
class VolunteerTest extends \PHPUnit_Framework_TestCase
{
/**
* #var Volunteer
*/
protected $object;
/**
* Sets up the fixture, for example, opens a network connection.
* This method is called before a test is executed.
*/
protected function setUp()
{
$this->object = new Volunteer();
}
public function testGetterAndSetter() {
$this->assertNull($this->object->setFirstName("Benny"));
$this->assertEquals("Benny", $this->object->getFirstName());
$this->assertNull($this->object->setLastName("Borko"));
$this->assertEquals("Borko", $this->object->getLastName());
$this->assertEquals('Borko, Benny', $this->object->getName());
}
}
Test results:
Failed asserting that Truckee\MatchingBundle\Entity\Volunteer Object
&0000000067c9c33f00000000680c6030 (
'id' => null ...
'credentialsExpireAt' => null ) is null.
The main goal of mocks is to test repositories or services. There is a easier way to test your entity:
class VolunteerTest extends \PHPUnit_Framework_TestCase {
/**
* #var Volunteer
*/
protected $object;
/**
* Sets up the fixture, for example, opens a network connection.
* This method is called before a test is executed.
*/
protected function setUp()
{
$this->object = new Volunteer();
}
public function testGetterAndSetter() {
$this->assertNull($this->object->setFirstName("Benny"));
$this->assertEquals("Benny", $this->object->getFirstName());
$this->assertNull($this->object->setLastName("Borko"));
$this->assertEquals("Borko", $this->object->getLastName());
}
}
The answer here is that the test is an inappropriate use of mock testing. After reading the article "An Introduction to Mock Object Testing" it became clear that the technique is to mock the dependencies of the system (object) under test, not the object itself. In the test I attempted, the Volunteer entity was the SUT so it should not have been mocked.

Mocking a REST Datasource in a Model TestCase for CakePHP

I am using the CakePHP-ReST-DataSource-Plugin Datasource for hitting a RESTful service in my model. This implies that the models will not have a database connection.
I have successfully accessed the services and would now like to write unit tests for the models. This is proving to be a daunting task since I cannot succeed to mock the datasource so that I do not hit the actual remote Service but rather return expected results for the tests.
<?php
App::uses('KnowledgePoint', 'Model');
class KnowledgePointTest extends CakeTestCase{
public $fixtures = array('app.knowledgepoint');
public $useDbConfig = 'RestTest';
private $KnowledgePoint;
public function setUp() {
parent::setUp();
$this->KnowledgePoint = ClassRegistry::init('KnowledgePoint');
/**
* This is the confusing part. How would I mock the datasource
so that I can mock the request method which returns the data
from the api?
*/
$this->KnowledgePoint->DataSource = $this->getMockForModel(
'RestSource',array('request'));
}
public function tearDown() {
parent::tearDown();
}
}
I would like to be able to mock the datasource and stub the request method to return data that would normally be returned from the remote service.
Kind regards,
Roland
Mocking the model and its getDataSource() method so that it returns your mocked datasource should theoretically work. Here's an example
App::uses('RestSource', 'Rest.Model/Datasource');
$DataSource = $this->getMock('RestSource', array('request'), array(array()));
$DataSource
->expects($this->any())
->method('request')
->will($this->returnValue('some custom return value'));
$Model = $this->getMockForModel('KnowledgePoint', array('getDataSource'));
$Model
->expects($this->any())
->method('getDataSource')
->will($this->returnValue($DataSource));
$Model->save(/* ... */);
In case you are wondering about the array(array()) for the datasource mock, this is required as the RestSource constructor doesn't supply a default value for the first argument (unlike the parent constructor).

Yii2 & Codeception - Enabling tester module in unit testing

I use Yii2 and build-in codeception and want to write unit tests. In the documentation of codeception I´ve found this example test:
function testUserCanBeActivatedWithValidKey()
{
// lookup for user with Eloquent API
$user = User::find($this->user_id);
// executing action
$isActivated = $user->activate('123456'));
// performing assertion
$this->assertTrue($isActivated);
// checking that data was actually saved into database
$this->tester->seeRecord('users', [
'id' => $this->user_id,
'is_active' => 1
]);
}
But when I´m trying to write the test myself, there is no $this->tester Object. How can I benefit from methods like seeInDatabase()?
Try this:
function testUserCanBeActivatedWithValidKey(WebGuy $I)
{
// lookup for user with Eloquent API
$user = User::find($this->user_id);
// executing action
$isActivated = $user->activate('123456'));
// performing assertion
$this->assertTrue($isActivated);
// checking that data was actually saved into database
$I->seeRecord('users', [
'id' => $this->user_id,
'is_active' => 1
]);
}
You get the tester object as a param from the test function. If it's a TestGuy, a WebGuy or something else, you have to look in your codeception.yml configuration.
class UserTest extends \yii\codeception\TestCase
{
/**
* #var \CodeGuy
*/
protected $tester;
function testUserCanBeActivatedWithValidKey()
{
// lookup for user with Eloquent API
$user = User::find($this->user_id);
// executing action
$isActivated = $user->activate('123456'));
// performing assertion
$this->assertTrue($isActivated);
// checking that data was actually saved into database
$this->tester->seeRecord('users', [
'id' => $this->user_id,
'is_active' => 1
]);
}
Override $tester and start using it.