proper way to test a service in Symfony 4, with database access - unit-testing

What would be the proper way to test a service in Symfony 4, which also accesses the database?
I am new in Symfony4 (before I developed for Symfony2) and I want to write my first test for a Service.
This service is writing via Entities / Doctrine / ORM in the database and each of my methods, I want to test is triggering a database save.
In Symfony 2 this was the case when I used rather KernelTestCase instead of the PHPUnit_Framework_TestCase because mocking the EntityManager was a pain in the ass and often I also wanted to check the result in the test db.
All examples for Symfony 4 only mention the KernelTestCase for testing Commands.
My class:
class UserPropertyService implements UserPropertyServiceInterface
{
public function __construct(EntityManager $em, LoggerInterface $logger)
{
....
}
....
}
My attempt at a Test:
class UserPropertyServiceTest extends KernelTestCase
{
/** #var UserPropertyService */
private $userPropertyService;
public function setUp()
{
self::bootKernel();
$client = static::createClient();
$container = $client->getContainer();
$this->userPropertyService = self::$container->get('app.user_management.user_property_service');
}
results in:
Cannot autowire service "App\Service\UserManagement\UserPropertyService": argument
"$em" of method "__construct()" references class "Doctrine\ORM\EntityManager"
but no such service exists.
Try changing the type-hint to one of its parents: interface "Doctrine\ORM\EntityManagerInterface",
or interface "Doctrine\Common\Persistence\ObjectManager".
What is the proper approach here? Which test class should I use?

This is how look like a service test (do not get your Container through the client, those containers are different)
By the way, you can't use static::createClient(); if you extend from KernelTestCase (misunderstanding with controller test and the WebTestCase class ?)
<?php
namespace App\Tests\Service;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
class UserPropertyServiceTest extends KernelTestCase
{
/** #var UserPropertyService */
private $myService;
public function setUp() {
self::bootKernel();
$this->myService = self::$kernel->getContainer()->get('app.user_management.user_property_service');
}
}

Related

cannot stub ReactiveMongoRepository using Mockito.when

I'm new to reactive programming. I want to write some test cases for a reactive mongo repository. I tried to stub some query methods and use step-verifier to check the response, but my test gets fail .
ItemReactiveRepository.java
public interface ItemReactiveRepository extends ReactiveMongoRepository<Item, String> {
Mono<Item> findByDescription(String description);
}
Item.java
#Document
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Item {
#Id
private String id;
private String description;
private Double price;
}
ItemReactiveRepositoryTest.java
#DataMongoTest
#RunWith(SpringRunner.class)
public class ItemReactiveRepositoryTest {
#Autowired
private ItemReactiveRepository itemReactiveRepository;
#Test
public void findById() {
Item itemForTest = new Item("ABC", "Samsung TV", 405.0);
Mockito.when(itemReactiveRepository.findById("ABC")).thenReturn(Mono.just(itemForTest));
StepVerifier.create(itemReactiveRepository.findById("ABC"))
.expectSubscription()
.expectNextMatches(item -> item.getPrice() == 405.0)
.verifyComplete();
}
}
Error I receive when running test
org.mockito.exceptions.misusing.MissingMethodInvocationException:
when() requires an argument which has to be 'a method call on a mock'.
For example:
when(mock.getArticles()).thenReturn(articles);
Also, this error might show up because:
you stub either of: final/private/equals()/hashCode() methods.
Those methods cannot be stubbed/verified.
Mocking methods declared on non-public parent classes is not supported.
inside when() you don't call method on mock but on some other object.
Are there any limitations to use stubbing when test reactive streams? Or any other standard mechanism to test above scenarios?
Instead of using #Autowired you have to prepare mock for Repository from import org.mockito.Mock;
#Mock
ItemReactiveRepository itemReactiveRepository;
As #Thomas has mentioned, you are not mocking instead using the actual MongoDB using DataMongoTest instead you have to get rid of this and just mock the methods and test your service layer. Autowired is expecting all default configuration and a bean which is prepared by the container for you to use which is not the case, you have to mock and use.
ItemReactiveRepository itemReactiveRepository=org.mockito.Mockito.mock(ItemReactiveRepository.class);
This worked for me.

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

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).

Unit test custom doctrine repository

I have a custom entity repository. For example, it looks like this:
namespace Foo\Repository;
use Doctrine\ORM\EntityRepository;
class Article extends EntityRepository
{
public function findRecent($limit)
{
$qb = $this->createQueryBuilder('a');
$qb->andWhere('a.publishDate IS NOT NULL')
->orderBy('a.publishDate', 'DESC')
->setMaxResults($limit);
return $qb->getQuery()->getResult();
}
}
I want to test in this case:
There is an ORDER BY in "recent"
There is a limit
The entity must have a publish date
I do not want to validate the SQL output of the query builder, since Doctrine can change the SQL between different versions. That will break my unit test. Therefore, my idea was this:
Create a mock of my repository
Create a mock of the query builder
Make sure $this->createQueryBuilder('a') returns the mocked query builder
Test for method calls on the query builder
In code:
namespace FooTest\Repository;
use PHPUnit_Framework_TestCase as TestCase;
class ArticleRepositoryTest extends TestCase
{
protected $qb;
protected $repository;
public function setUp()
{
$this->qb = $this->getMockBuilder('Doctrine\ORM\QueryBuilder')
->disableOriginalConstructor()
->getMock();
$this->repository = $this->getMockBuilder('Foo\Repository\Article')
->disableOriginalConstructor()
->getMock();
$this->repository->expects($this->once())
->method('createQueryBuilder')
->with($this->equalTo('a'))
->will($this->returnValue($this->qb));
}
public function testFindRecentLimitsToGivenLimit()
{
$limit = '1';
$this->qb->expects($this->any())
->method('setMaxResults')
->with($this->equalTo($limit));
$this->repository->findRecent($limit);
}
public function testFindRecentOrdersByPublishDate()
{
$this->qb->expects($this->any())
->method('andWhere')
->with($this->equalTo('a.publishDate'), $this->equalTo('DESC'));
$this->repository->findRecent(1);
}
}
This findRecent() call however never calls createQueryBuilder internally. PhpUnit points out:
FooTest\Repository\ArticleRepositoryTest::testFindRecentLimitsToGivenLimit
Expectation failed for method name is equal to when invoked 1 time(s).
Method was expected to be called 1 times, actually called 0 times.
I think I did something wrong in creating the repository mock. How can I make sure this approach works? Or if there is a better alternative, what is that?
It looks to me like you are mocking the Repository you are trying to test, so findRecent() is indeed mocked and will return null.
You should be able to use the real repository instance.
The solution I found to testing subclassed repositories is to add a call to setMethodsExcept() when building the mock.
So you would modify your code within setUp() above like so:
$this->repository = $this->getMockBuilder('Foo\Repository\Article')
->disableOriginalConstructor()
->setMethodsExcept([
// Insert any overridden/implemented functions here, in your case:
'findRecent',
])
->getMock();