PHPUnit withConsecutive strange behaviour - php

Consider the next test:
class User
{
}
class UserRepository
{
public function getByName($name)
{
}
public function getByUser(User $user)
{
}
}
class UserController
{
private $userRepository;
public function __construct(UserRepository $userRepository)
{
$this->userRepository = $userRepository;
}
public function findByNames($name1, $name2)
{
$this->userRepository->getByName($name1);
$this->userRepository->getByName($name2);
}
public function findByUsers($user1, $user2)
{
$this->userRepository->getByUser($user1);
$this->userRepository->getByUser($user2);
}
}
class WithConsecutiveTest extends \PHPUnit_Framework_TestCase
{
/**
* This test is fails if some of "Alice" or "Bob" string changed. This is expected behaviour.
*/
public function testWithConsecutiveOnStrings()
{
$name1 = 'Alice';
$name2 = 'Bob';
$userRepository = $this->createMock(UserRepository::class);
$userRepository
->expects($this->exactly(2))
->method('getByName')
->withConsecutive(
[$name1], // change to $name2 and test fails
[$name2]
);
$userController = new UserController($userRepository);
$userController->findByNames($name1, $name2);
}
/**
* This test is NOT fails if in "withConsecutive()" call $user1 changed to $user2. This is unexpected behaviour.
*/
public function testWithConsecutiveOnObjects()
{
$user1 = $this->createMock(User::class);
$user2 = $this->createMock(User::class);
$this->assertEquals($user1, $user2);
$this->assertNotSame($user1, $user2);
$userRepository = $this->createMock(UserRepository::class);
$userRepository
->expects($this->exactly(2))
->method('getByUser')
->withConsecutive(
[$user1], // change to $user2 and test is also passed
[$user2]
);
$userController = new UserController($userRepository);
$userController->findByUsers($user1, $user2);
}
}
First test with string arguments for "withConsecutive()" is works correctly, but second test with objects do some magic: something similar to weak comparison of two objects is here, so second test is passed in any case.
I tried with "[$this->callback(function($user1arg) use ($user1)){return $user1arg === $user1}]" constraint - this works well, but write a lot of this constraint is a some kind of monkey job.
Maybe it may have a more simple solution, instead of writing a lot of constraints with $this->callback(...) for objects?

Try this code:
$userRepository
->expects($this->exactly(2))
->method('getByUser')
->withConsecutive(
[$this->identicalTo($user1)],
[$this->identicalTo($user2)]
);

Not everything in PHPUnit is perfect, but this should work for You. Of course property 'name' is only an example. If user does not holds any behaviour, You don't need to mock it.
class User
{
private $name;
public function setName($name)
{
$this->name = $name;
}
public function getName($name)
{
return $this->name;
}
}
test method
public function testWithConsecutiveOnObjects()
{
$user1 = new User();
$user1->setName('Alice');
$user2 = new User();
$user2->setName('Bob');
$this->assertNotEquals($user1, $user2);
$this->assertNotSame($user1, $user2);
$userRepository = $this->createMock(UserRepository::class);
$userRepository
->expects($this->exactly(2))
->method('getByUser')
->withConsecutive(
[$user1], //we are comparing values here, not references
[$user2]
);
$userController = new UserController($userRepository);
$userController->findByUsers($user1, $user2);
}

Related

How to use autowired services when using PhpUnit?

Was testing an application and was repeatable getting the infamous new entity not configured to cascade persist error. I was surprised since I wasn't even creating new entities, and after digging into it, it appears to be relate to using different instances of the EntityManager object (I have confirmed that they are working with the same database, however) which I guess makes sense since each test will have a transaction applied. The only way I was able to get rid of the errors was to use the entityManager in the container instead of the autowired ones. While it works, it is a bit of a kludge and I would like to know the right way of doing this. Thank you
namespace App\Tests;
use ApiPlatform\Symfony\Bundle\Test\ApiTestCase;
class MyTest extends ApiTestCase
{
/**
* #dataProvider getData
*/
public function testWhichDoesNotWork(int $id, string $class)
{
$service = static::getContainer()->get(MyService::class);
$user = $service->getUser();
$randomEntity = $service->getRandomEntity($user->getTenant(), $class);
$randomEntity->setSomething('something');
$service->saveEntity($randomEntity);
}
/**
* #dataProvider getData
*/
public function testWhichWorks(int $id, string $class)
{
$service = static::getContainer()->get(MyService::class);
$em = static::getContainer()->get(EntityManagerInterface::class);
$user = $service->getUser();
$randomId = $service->getRandomEntityId($user->getTenant(), $class);
$randomEntity = $em->getRepository($class)->find($randomId);
$randomEntity->setSomething('something');
$em->persist($randomEntity);
$em->flush();
}
/**
* #dataProvider getData
*/
public function testAnotherWhichWorks(int $id, string $class)
{
$service = static::getContainer()->get(MyService::class);
$service->setNewEntityManager(static::getContainer()->get(EntityManagerInterface::class));
$user = $service->getUser();
$randomEntity = $service->getRandomEntity($user->getTenant(), $class);
$randomEntity->setSomething('something');
$service->saveEntity($randomEntity);
}
public function getData(): array
{
return [
[123, SomeClass::class]
];
}
}
namespace App\Test\Service;
final class MyService
{
public function __construct(private EntityManagerInterface $entityManager)
{}
public function setNewEntityManager(EntityManagerInterface $entityManager):self
{
$this->entityManager = $entityManager;
return $this;
}
public function getDatabase():string
{
return $this->entityManager->getConnection()->getDatabase();
}
public function getUser(int $id):User
{
return $this->entityManager->getRepository(User::class)->find($id);
}
public function getRandomId(Tenant $tenant, string $class):int
{
$meta = $this->entityManager->getClassMetadata($class);
$_sql = 'SELECT %s FROM public.%s WHERE tenant_id=? OFFSET floor(random() * (SELECT COUNT(*) FROM public.%s WHERE tenant_id=?)) LIMIT 1;';
$sql = sprintf($_sql, $meta->getSingleIdentifierFieldName(), $meta->getTableName(), $meta->getTableName());
return $this->entityManager->getConnection()->prepare($sql)->execute([$tenant->getId(), $tenant->getId()])->fetchOne();
}
public function getRandomEntity(Tenant $tenant, string $class):object
{
return $this->entityManager->getRepository($class)->find($this->getRandomId($tenant, $class));
}
public function saveEntity(object $entity):self
{
$this->entityManager->persist($entity);
$this->flush();
return $this;
}
}
services:
app.test.my.service:
alias: App\Test\Service\MyService
public: true

PHP class array object method access issue

I wrote a class
class User {
private $cars = array(); //store class Car 's object
public function getCars()
{
return $this->cars;
}
public function setCars($cars)
{
$this->cars = $cars;
}
}
class Car{
private $model;
public function getModel()
{
return $this->model;
}
public function setModel($model)
{
$this->model = $model;
}
}
$user = new User();
$cars = $user->getCars();
$cars[0]->getModel();
When I try to access getModel() php report "Call to undefined method stdClass::getModel()" .
Is there the best practice to deal with such case?
Edit:I filled the getter and setter. In fact, It's generated by phpstorm.
Edit:I tried again and it works well with the demo code below. The original code is too complicated to show. Maybe I caused by my misunderstanding of copying by value and by reference of array.
Please ignore this question. sorry.
class User {
private $cars = array(); //store class Car 's object
public function getCars()
{
return $this->cars;
}
public function setCars($cars)
{
$this->cars = $cars;
}
}
class Car{
private $model;
public function getModel()
{
return $this->model;
}
public function setModel($model)
{
$this->model = $model;
}
}
$user = new User();
$car = new Car();
$car->setModel("Ford");
$arr = $user->getCars();
array_push($arr,$car);
$user->setCars($arr);
foreach($user->getCars() as $car) {
var_dump($car->getModel());
}
You haven't shown your [Getter Setter ] code. You need to create one with something like:
public function setCars($val){
$this->cars = $val;
}
public function getCars(){
return $this->cars;
}
The same applies for getModel()

php oop code syntax, unused instances

I'm trying to accomplish the following syntax in my code:
$data = new Data();
$user = $data -> user -> get(1);
$product = $data -> product -> get(1);
By using:
class Data {
public $user = null;
public $product = null;
public $a = null;
...
function __construct() {
this -> user = new User();
this -> product = new Product();
this -> a = new A();
...
}
}
The problem with the code is that I will have lots of unused instances inside the data class because I will not use them all in specific scenarios. How can I prevent this?
At a very basic level, you can do something like this, you define a getter for the user property, and the object only gets instantiated when you call it for the first time.
class Data {
protected $user = null;
public function user()
{
if ($this->user === null) {
$this->user = new User();
}
return $this->user;
}
}
You could use aggregation, which means that you pass an object into the class, that way the class is getting either null or the object and you save resources by not initializing everything at once. Here's a decent post about it (not mine).
It's basically this:
class Test {
public $a = '';
public function __construct($object) {
$this->a = $object;
}
}
I would say you could try something like this:
class ThisOne{
protected $user = null;
public function user()
{
if ($this->user === null) {
$this->user = new User();
}
return $this->user;
}
}
The getter only gives you an object the first time it is called!

PHPUnit Stub does not return needed value

I have the following unit test but I don't get back the needed values. Maybe I don't understand how this works correctly.
class TestClass
{
public function getData()
{
$id = 1123;
return $id;
}
}
class Test_ClassTesting extends PHPUnit_Framework_TestCase
{
public function test_addData()
{
$stub = $this->getMock('TestClass');
$stub
->expects($this->any())
->method('getData')
->will($this->returnValue('what_should_i_put_here_to_get id from TESTCLASS'));
$y = $stub->getData();
}
}
As the commenters have said, you simply return the value desired.
class TestClass
{
public function getData()
{
$id = 1123;
return $id;
}
}
class Test_ClassTesting extends PHPUnit_Framework_TestCase
{
public function test_addData()
{
$stub = $this->getMock('TestClass'); // Original Class is not used now
$stub
->expects($this->any())
->method('getData')
->will($this->returnValue(4444)); // Using different number to show stub works, not actual function
$this->assertEquals(4444, $stub->getData());
}
public function test_addDataWithoutStub()
{
$object = new TestClass();
$this->assertEquals(1123, $object->getData());
}
}

Polymorphism/Resource loader with Zend

I've never worked before with polymorphism. I just heard about it when this question came up.
I have a little backend with 2 permissions. Admin/Normal User. Depending on the permission, i want to display a different navigation, less or more options on the forms etc. But i don't want to create a form for each permission but rather disable the elements i don't need etc.
How would i go with that?
At the moment, i'm using something like that: (Which isn't really polymorphism)
<?php
class My_Resources_ResourceLoader extends Zend_Application_Resource_ResourceAbstract {
public $templateForm = null;
public $customerForm = null;
function init() {
$permission = 'admind';
if($permission == 'admin') {
$this->templateForm = new Application_Form_newTemplate;
} else {
$form = new Application_Form_newTemplate;
$form->removeElement('newTemplate_customer');
$this->templateForm = $form;
}
return $this;
}
}
And in my controller e.g.
<?php
$bootstrap = $this->getInvokeArg('bootstrap');
$xx = $bootstrap->getResource('ResourceLoader');
$this->view->test = $xx->templateForm;
The roles never gonna change. This will probably be okay but isn't the very best solution. What would be a better approach to this?
I've thrown away the approach above and now use real polymorphism like this:
at Application/Model got an interface like:
And 2 Classes like:
<?php
class Application_Model_TemplateUser implements Application_Model_TemplateInterface {
private $table = null;
private $row = null;
private $id = null;
private $formValues = null;
function __construct() {}
public function exist() {}
public function save() {}
public function getCustomerId($name) {}
public function update() {}
public function getForm() {
$form = new Application_Form_newTemplate;
$form->removeElement('newTemplate_customer');
return $form;
}
}
And
<?php
class Application_Model_TemplateAdmin implements Application_Model_TemplateInterface {
private $table = null;
private $row = null;
private $id = null;
private $formValues = null;
function __construct() {}
public function exist() {}
public function save() {}
public function getCustomerId($name) {}
public function update() {}
public function getForm() {
return new Application_Form_NewTemplate();
}
}
In my Controller i do:
<?php
$permission = 'User'; //TODO: Get from Session
$class = 'Application_Model_Template' . $permission;
$xx = new $class;
$form = $xx->getForm();
$this->view->test = $form;
This are just examples. But i think like that I'm really on a better way. Maybe i'm going to use abstract classes since i'm using Zend_Db-Table_Row, which is always the same for updating a row, so it would make more sense using a abstract class instead of an interface.
Nice article about Polymorphism in PHP: http://net.tutsplus.com/tutorials/php/understanding-and-applying-polymorphism-in-php/

Categories