How to unit test createQueryBuilder - symfony

I have this method:
public function findAllBy(?string $categoryId): array
{
$qb = $this->entityManager->createQueryBuilder()
->select('p')
->from(Product::class, 'p');
if (null !== $categoryId) {
$qb->from(Category::class, 'cc')
->join(CategoryProduct::class, 'cp', Join::WITH, 'p.id = cp.product_id')
->join(Category::class, 'c', Join::WITH, 'cp.category_id = c.id')
->where('cc.id = :id')
->andWhere('cc.left <= c.left')
->andWhere('cc.right >= c.right')
->setParameter('id', $categoryId, 'uuid');
}
return $qb->getQuery()->getResult();
}
I am trying to test it this way ( obviously not the correct one ):
public function testFindAllProductsByFilters():void
{
$entityRepositoryMock = $this->createMock(EntityRepository::class);
$entityManagerMock = $this->createMock(EntityManagerInterface::class);
$entityManagerMock->expects($this->once())
->method('getRepository')
->with(Product::class)
->willReturn($entityRepositoryMock);
$entityManagerMock->expects($this->once())
->method('createQueryBuilder')
->willReturn($entityRepositoryMock);
$this->repository = new DoctrineProductRepository($entityManagerMock);
$this->assertIsArray($this->repository->findAllBy(ProductFactory::CATEGORY_ID_FIRST));
}
The error I get:
1)
App\Tests\Unit\Infrastructure\Domain\Model\Product\DoctrineProductRepositoryTest::testFindAllProductsByFilters
Error: Call to a member function from() on null
Is this piece of code even testable by Unit Test ?

As you should not mock what you don't own, my suggestion is to avoid unit tests in this kind of scenarios.
Moreover I would not use (abuse) a mock (a test double in general) as you're testing the implementation and not the behaviour of your SUT.
Let's see an example
class Foo()
{
public function doSomething(): int
{
// some heavy logic here
}
}
class Bar()
{
public function doSomething(Foo $foo): int
{
$result = $foo->doSomething();
// other elaboration upon $result
}
}
That's a deliberately trivial example of course. If you use a test double in Bar test, you would write something like`
$fooMock->expects($this->once())
->method('doSomething')
->willReturn(1);
Let's say that Foo changes it's public API, renaming doSomething to doSomethingElse. What happens? You need to change the test aswell even if Foo behaviour didn't changed at all.
As said before it's a trivial example but should give you an idea.

Related

Symfony - How to transfrom these 2 functions in DRY way?

I got a service where I got 2 functions doing a part where the same Repository is called but with different queries and different arguments passed.
Basically, I have this first function:
public function function1($date, Entity1 $entity1)
{
$repos = $this->em->getRepository(EntityCommon::class)->findAllByXXX($entity1, $date);
...//Operations unique to this function
$results = new functionDRY(); //part common to both starting with the foreach on $repos
}
and this second function:
public function function2($date, Entity2 $entity2)
{
$repos = $this->em->getRepository(EntityCommon::class)->findAllByYYY($entity2, $date);
...//Operations unique to this function
$results = new functionDRY(); //part common to both starting with the foreach on $repos
}
I wanted to create a third function where I would use DRY logic since a big chunk of code would be similar except in the query needed in the repository:
public function functionDRY()
{
foreach ($repos as $repo) {// only operations common to both
$somethings = $repo->getSomething()->getValues();
foreach ($somethings as $something) {
}
}
}
How could I write the 3rd method since on I use foreach on $repos but it's defined in the 2 other functions?
Also, I tried to call the functionDRY in the 2 first functions but it doesn't work. I tried using directly functionDRY() but it didn't work either.
How can I call the function in both function1 and function2?
That might be rookie mistakes I would avoid in the future.
You may want to reverse your logic. The sample below would use the method AService::functionDRY for both entities and the method gets $repos from the appropriate function1 or function2 based on the class of the entity.
class AService
{
public function function1($date, Entity1 $entity1)
{
$repos = $this->em->getRepository(EntityCommon::class)->findAllByXXX($entity1, $date);
//Operations unique to this function
return $repos;
}
public function function2($date, Entity2 $entity2)
{
$repos = $this->em->getRepository(EntityCommon::class)->findAllByYYY($entity2, $date);
//Operations unique to this function
return $repos;
}
public function functionDRY($date, $entity)
{
$repos = [];
if (is_a($entity, Entity1::class)) {
$repos = $this->function1($date, $entity);
}
elseif (is_a($entity, Entity2::class)) {
$repos = $this->function2($date, $entity);
}
foreach ($repos as $repo) {// only operations common to both
$somethings = $repo->getSomething()->getValues();
foreach ($somethings as $something) {
}
}
}
}

PhpUnit testing repositories Symfony findOneBy

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

phpunit replace the method return

Here is my code. How can I modify the function's return result.
class Replace {
public function add($a){
// how can I replace $this->double($anyNum) return value
return $a + $this->double($a);
}
public function double($a){
return $a + $a;
}
}
class ReplaceTest extends PHPUnit_Framework_TestCase {
public function testadd(){
$replace = $this->getMock('Replace');
// I want to control the method's return,
//no matter what num passed to it from function add
$replace->expects($this->any())->method('double')->will($this->returnValue(15));
// this return null
$data = $replace->add(6);
// this is the expected result I want,
// and when I set the returnValue(21),I hope the expected result is 27
$this->assertEquals(21, $data);
}
}
How can I modify my code, thank you very very much.
If specified, the second parameter of getMock() tells phpunit which methods to replace. Thus, in your case:
$replace = $this->getMock('Replace',array('add'))
Although in the example you gave, I'd recommend not mocking the Replace class, adding a test for the double() method will suffice, as if the double() test passes, you can rely on that method's result.

codeigniter recursive model function returning blank but when printing it in the model showing properly

codeigniter recursive model function returning blank but when printing it in the model showing properly
here is my code,
for controller
$commision_arr=$this->billing_model->root_commision($category_manager['id']);
and in the model
public function root_commision($id)
{
$sql="SELECT * FROM tbl_mst_category WHERE id = '".$id."'";
$query = $this->db->query($sql);
$row=$query->row_array();
if($row['parent']!=0)
{
$this->root_commision($row['parent']);
}
else
return $row;
}
recursion is tricky huh?
i think the problem is that you were only returning the id for the deepest element, but not returning that to the calling method -- so it would only work for the case where the parent id was called. i can't test the code below, but it should point you in the right direction. NB, it returns the row as an object, not as an array as your code does.
On a more academic note, if the table is large it may be better to pre-calculate these root ids for each of these categories. it will make the query much faster -- recursion is not fast. look at transitive closures
public function root_commision($id,$root_found = FALSE)
{
// returns FALSE if the id is not found, or the parent row.
$query = $this->db->get_where('tbl_mst_category', array('id' => $id));
if ($query->num_rows() > 0 )
{
$row = $query->first_row();
if (($row->parent ) != 0 )
{
return $this->root_commision($row_id);
}
else
{
return $row;
}
}
else
{
return FALSE;
}
}
you have to return function at the calling time then only you can get
the value of recursive function just add "return" keyword before function call.
public function root_commision($id)
{
$sql="SELECT * FROM tbl_mst_category WHERE id = '".$id."'";
$query = $this->db->query($sql);
$row=$query->row_array();
if($row['parent']!=0)
{
return $this->root_commision($row['parent']);
}
else
return $row;
}

Symfony2 set class variable with init or construct methods

Have recently been using Symfony2 after using ZF for some time.
I am having problems trying to do something relatively simple, I think.
The following code is within a controller:
private $current_setid = "";
public function __construct() {
$current_set = $this->getCurrentSet();
if ($current_set == "") {
return $this->redirect($this->generateUrl('selectset'));
}
$this->current_setid = $current_set;
}
public function getCurrentSet() {
$session = $this->get("session");
$set = $session->get('set');
return $set;
}
public function setCurrentSet($setid) {
$session = $this->get("session");
$session->set('set', "$setid");
}
If I use __construct() I get errors like:
Fatal error: Call to a member function get() on a non-object in
I have tried using __init() and init() both of which do not seem to get called.
Can anyone point me in the right direction? Is there a simple way to do this or do I have to look into event listeners?
Have you tried getting your session like they do in official documentation?
$session = $this->getRequest()->getSession();
$foo = $session->get('foo');
Basically get fetch dependencies from container and container in the Controller is injected using setter dependency injection. You just not have container in the time of __construct yet.
Just ended up opting for placing a check in every method in the class. Seems silly to have to do that but I find I often have to do that in Symfony2 with the lack of init, postDispatch type methods like ZF has.
Even trying to remove the check to another method was counter productive as I still had to check the return from that method as $this->redirect does not seem to work unless it is within an Action method. For example:
public function isSetSet() {
$current_set = $this->getCurrentSet();
if ($current_set == "") {
$url = $this->generateUrl('selectset');
return $this->redirect($url);
}
return TRUE;
}
public function someAction() {
$check = $this->isSetSet();
if($check != TRUE){
return $check;
}
...
}
So each method needs that 4 line check but the whole check can be done in 4 lines anyway so no need for that extra method:
public function anotherAction() {
$current_setid = $this->getCurrentSet();
if ($current_setid == "") {
return $this->redirect($this->generateUrl('selectset'));
}
...
}

Resources