I am net to phpunit in Symfony 3 and I'm wondering what exactly should I follow when testing a repository. For example, I have the following repo function:
/**
* {#inheritdoc}
*/
public function findAdminRole()
{
$role = $this->repository->findOneBy(['name' => Role::ADMIN]);
if (null === $role) {
throw new NotFoundException();
}
return $role;
}
What exactly a test for this would look like? Should I test that the function findOneBy and the NotFoundException are called or to get real data values? I am kinda stuck here.
Thank you.
ok so findOneBY returns either Null or an Object, you can set up sample data which would return either Null or say a role object and test for that, here's something in my opinion to help you get started.
so, in the setup i would mock the repository:
$this->mockRepository = $this
->getMockBuilder('path to the respository')
->disableOriginalConstructor()
->setMethods(array('if you want to stub any'))
->getMock();
$this->object = new class( //this is the class under test
// all your other mocked services, ex : logger or anything else
)
now we have a mock of the repo, lets see how the sample tests would look like
1st test
public function findAdminRoleTestReturnsException(){
$name = ['name' => Role::ABC]; // consider this will return Null result from DB due to whatever reason
$exception = new NotFoundException();
// do whatever other assertions you need here
$this->mockRepository->expects($this->any())
->method('findOneBY')
->with($name)
->will($this->returnValue(null));
// Now call the function
$role = $this->object->findAdminRole();
$this->assertEquals($exception, $role);
}
in the same manner above you can write another test like:
2nd test
public function findAdminRoleTestReturnsNewRole(){
$name = ['name' => Role::ADMIN] // this will find the entry in theDB
$ testRoleObject = new Role();
$this->mockRepository->expects($this->any())
->method('findOneBY')
->with($name)
->will($this->returnValue($testRoleObject));
// Now call the function
$role = $this->object->findAdminRole();
$this->assertEquals($testRoleObject, $role);
}
hope this helps
Related
A method from my MyClass class I'd like to test looks like this:
public function needs()
{
$domains = $this->em->getRepository(WebDomain::class)->findBy(array(
'client' => $this->client
));
$hosting = $this->em->getRepository(WebHosting::class)->findBy(array(
'client' => $this->client
));
if($domains !== null && $hosting !== null){
return true;
}
return false;
}
Looking at the documentation of Symfony I create a test like this:
public function testNeeds()
{
$em = $this->createMock(ObjectManager::class);
$client = new Client();
/**
* Add WebHosting to Client
*/
$webHosting = new WebHosting();
$webHosting->setClient($client);
/**
* Create a new WebDomain for Client/WebHosting
*/
$webDomain = new WebDomain();
$webDomain->setClient($client);
$webDomain->setWebHosting($webHosting);
I know how to create a mocked repository (the needed $domains for example):
$domains = $this->createMock(ObjectRepository::class);
$domains->expects($this->any())
->method('findBy')
->willReturn($client->getWebDomain());
$em->expects($this->any())
->method('getRepository')
->willReturn($domains);
$myClass = new MyClass($client, $em);
So from my understanding, this creates a mock that whenever the method findBy is called, return the $domains, but what do I have to add in order to return the needed $hosting?
I suspect it has something to do with the $this->any(), I assume I have to narrow it down to expects(WebDomain::class) (which does not work ofc).
Since I am fairly new to UnitTests in Symfony (and in general) pointing me to the right manual might help as well. Thank you!
In you case you should return different Repository based on argument passed to getRepository method. Something like:
$emMock
->method('getRepository')
->will($this->returnValueMap([
[WebDomain::class, $webDomainRepositoryMock),
[WebHosting::class, $webHostingRepositoryMock)
]));
Note: remember to configure findBy for both repositories.
My controller code:
public function postFilesAction(Request $request)
{
$validator = $this->get('validator');
$requestCredentials = RequestCredentials::fromRequest($request);
$errors = $validator->validate($requestCredentials);
...
validate method in RequestCredentials (Callback constraint).
/**
* #Assert\Callback(payload = {"errorCode" = "FILE_FILE_URL"})
*/
public function validate(ExecutionContextInterface $context)
{
if (! ($this->fileExistsAndValid() || $this->fileUrlExistsAndValid())) {
$context->buildViolation('Neither file nor file_url is present.')->addViolation();
}
}
Callback works as expected, but the value of $constraintViolation->$constraint->$payload is null.
When I'm trying to use payload in other Constraints (NotBlank, for example), it works (I can see it in ConstraintViolation object).
Is it Symfony bug or am I doing somethings wrong? Should I use some other solution to my problem? (I need to check if there's at least one of two fields (file or file_url) present in request).
In Symfony 3.0 you cannot easily access the payload in the callback when using the Callback constraint. Starting with Symfony 3.1, the payload will be passed as an additional argument to the callback (see https://github.com/symfony/symfony/issues/15092 and https://github.com/symfony/symfony/pull/16909).
I managed to solve this problem with following code in the assertion:
/**
* #Assert\Callback(payload = {"error_code" = "1"}, callback = "validate", groups = {"Default", "RequestCredentials"})
*/
public function validate(ExecutionContextInterface $context)
{
// some validation code
}
I think the problem was because of the Symfony Callback constraint constructor:
public function __construct($options = null)
{
// Invocation through annotations with an array parameter only
if (is_array($options) && 1 === count($options) && isset($options['value'])) {
$options = $options['value'];
}
if (is_array($options) && !isset($options['callback']) && !isset($options['groups'])) {
$options = array('callback' => $options);
}
parent::__construct($options);
}
When it is given $options = ['payload' => [...]] (what happened in my case) it turns it into $options = ['callback' => ['payload' => [...]]]
and then '$payload' data becomes inacessable in ConstraintViolation object.
But I'm still not sure whether it's Symfony imperfection or me not getting something and using it wrong.
Good morning,
Is it exist an function where I pass an entity and the propertyName and return me the mappedBy,inversedBy and absoluteClassName of an Entity.
The goal is to use the __call to create automatic getteur/setteur and addFucntion bidirectionnal.
I don't want to use generates Entities I want all getteur,setteur and add Function use __call.
But i can"t do an addBirectionnal if i don't know if the relation is many to many or one to many and if i don't know the name of the mappedBy.
my code:
public function __get($p){
return $this->$p;
}
public function __set($p,$v){
$this->$p = $v;
return $this;
}
public function __call($name,$arguments){
if(substr($name,1,3)=='et')
$name2 = substr(3);
if($name[0] == 'g'){
return $this->$name2;
}else{//substr($name,0,1) == 's'
$this->$name2 = $arguments[0];
/*for a one to one*/
/*$mappedByName= getmappedByOrInversedBy(get_class($name),$name2);
if($mappedByName){
$this->$name->$mappedByName = $this;/
}*/
return $this;
}
}
}
I need getmappedByOrInversedBy, thanks.
edit: I try this
public function test(){
$str = "AppBundle\Entity\Group";
$mapping = new \Doctrine\ORM\Mapping\ClassMetadataInfo($str);
$d = $mapping->getAssociationMappedByTargetField('trad');
var_dump($d);
return $this->render('default/index.html.twig', array(
'base_dir' => realpath($this->getParameter('kernel.root_dir').'/..'),
));
}
class Group
{
...
/**
* #ORM\OneToOne(targetEntity="Traduction",inversedBy="grp")
*/
protected $trad;
}
Result : Undefined index: trad
The ClassMetadataInfo is what you are looking for.
Creates an instance with the entityName :
$mapping = new \Doctrine\ORM\Mapping\ClassMetadataInfo($entityNamespaceOrAlias);
Then, get the informations you want :
Get all association names: $mapping->getAssociationNames();
Get the join column of an association:
$mapping->getSingleAssociationJoinColumnName($fieldName);
Get the mappedBy column of an association:
$mapping->getAssociationMappedByTargetField($fieldName);
...
Look at the class to know which method you can access.
Hopes it's what you expect.
EDIT
As you can access the EntityManager (i.e. from a controller), use :
$em = $this->getDoctrine()->getManager();
$metadata = $em->getClassMetadata('AppBundle:Group');
To be sure there is no problem with your entity namespace, try :
print $metadata->getTableName();
To retrieve the associations of the entity, use :
$metadata->getAssociationNames();
And to get the mapping informations of an existing association, use :
$metadata->getAssociationMapping($fieldName);
And to get all the association mappings of your entity, use:
$metadata->getAssociationMappings();
I would like to call a getter with the stored fieldname from the database.
For example, there are some fieldnames store like ['id','email','name'].
$array=Array('id','email','name');
Normally, I will call ->getId() or ->getEmail()....
In this case, I have no chance to handle things like this. Is there any possibility to get the variable as part of the get Command like...
foreach ($array as $item){
$value[]=$repository->get$item();
}
Can I use the magic Method in someway? this is a bit confusing....
Symfony offers a special PropertyAccessor you could use:
use Symfony\Component\PropertyAccess\PropertyAccess;
$accessor = PropertyAccess::createPropertyAccessor();
class Person
{
private $firstName = 'Wouter';
public function getFirstName()
{
return $this->firstName;
}
}
$person = new Person();
var_dump($accessor->getValue($person, 'first_name')); // 'Wouter'
http://symfony.com/doc/current/components/property_access/introduction.html#using-getters
You can do it like this :
// For example, to get getId()
$reflectionMethod = new ReflectionMethod('AppBundle\Entity\YourEntity','get'.$soft[0]);
$i[] = $reflectionMethod->invoke($yourObject);
With $yourObject being the object of which you want to get the id from.
EDIT : Don't forget the use to add :
use ReflectionMethod;
Hope this helps.
<?php
// You can get Getter method like this
use Doctrine\Common\Inflector\Inflector;
$array = ['id', 'email', 'name'];
$value = [];
foreach ($array as $item){
$method = Inflector::classify('get_'.$item);
// Call it
if (method_exists($repository, $method))
$value[] = $repository->$method();
}
I am struggling so much with Mock. I try to do some tests on my website (Symfony2).
Here is my error :
There was 1 failure:
1) L3L2\EntraideBundle\Tests\Controller\InboxControllerTest::testGetNbTotalMessagePasDejaVu
Expectation failed for method name is equal to <string:getNbTotalMessagePasDejaVu> when invoked 1 time(s).
Method was expected to be called 1 times, actually called 0 times.
I saw few examples on the internet with the argument $entityManager like an attribute of the class (but I have no time to change it anymore). I hope it's not the origin of the problem...
Here is my code :
InboxController :
public function getNbTotalMessagePasDejaVu(ObjectManager $entityManager = null)
{
if($entityManager == null)
$entityManager = $this->getDoctrine()->getEntityManager();
$repository = $entityManager->getRepository('L3L2EntraideBundle:Message');
$nbMessagePasDejaVu = $repository->getNbTotalMessagePasDejaVu(1); //change 1 to $this->getUser()->getId()
return $nbMessagePasDejaVu;
}
InboxControllerTest :
class InboxControllerTest extends \PHPUnit_Framework_TestCase
{
public function testGetNbTotalMessagePasDejaVu()
{
$msgRepository = $this
->getMockBuilder("\Doctrine\ORM\EntityRepository")
->disableOriginalConstructor()
->getMock();
$msgRepository->expects($this->once())
->method('getNbTotalMessagePasDejaVu')
->will($this->returnValue(0));
$entityManager = $this->getMockBuilder('\Doctrine\Common\Persistence\ObjectManager')
->disableOriginalConstructor()
->getMock();
$entityManager->expects($this->once())
->method('getRepository')
->will($this->returnValue($msgRepository));
$msg = new InboxController();
$this->assertEquals(0, $msg->getNbTotalMessagePasDejaVu($entityManager));
}
}
Does anyone have an idea ? Thank you !
The problem is in the following line section:
$msgRepository->expects($this->once())
->method('getNbTotalMessagePasDejaVu')
->will($this->returnValue(0));
By declaring ->expects($this->once()) you say that this method is called once per test case. But if you don't use it, it will trigger an exception.
If you don't need the method to be triggered excactly once per test, use ->expects($this->any()) instead.
If you only want to stub the method and make it return something you can do what you already have. But if want to verify that the method was actually called you have to 'mock' it. This is what setMethods() method is for.
$entityManager = $this->getMockBuilder('\Doctrine\Common\Persistence\ObjectManager')
->disableOriginalConstructor()
->setMethods(array('getRepository'))
->getMock();
Now you can say ->expects($this->once())->method('getRepository').