Connect.php
class Connect
{
private $dbName;
public function __construct($dbName)
{
$this->dbName = $dbName;
}
public function getQuery()
{
return $this->dbName;
}
}
ConnectFacade.php
class ConnectFacade
{
public $dbA;
public $dbB;
public $dbC;
public $dbD;
public function __construct()
{
$this->dbA = new Connect('connect to database a');
$this->dbB = new Connect('connect to database b');
$this->dbC = new Connect('connect to database c');
$this->dbD = new Connect('connect to database d');
}
}
Member.php
class Member
{
private $connect;
public function __construct(ConnectFacade $connect)
{
$this->connect = $connect;
}
public function data()
{
return $this->connect->dbA->getQuery();
}
}
index.php
require 'vendor/autoload.php';
echo (new Member(new ConnectFacade))->data();
In my case, I have many tables from many databases need to be connected in one class, therefore I need to declare many connections with different connection name very often, so I put it to ConnectFacade for saving time, I know it's kind of complicated, but that's the structure of my company, this sample code will show connect to database a on index.php, of course in reality the connection is doing a lot of stuffs, my question is I don't know how mock on this situation, this is what I tried
use Mockery as m;
class MemberTest extends PHPUnit_Framework_TestCase
{
public function testData()
{
$connect = m::mock('ConnectFacade');
$connect->shouldReceive('dbA->getQuery')
->once()
->andReturn(true);
$actual = (new Member($connect))->data();
$this->assertTrue($actual);
}
public function tearDown()
{
m::close();
}
}
and I got Call to a member function getQuery() on a non-object in D:\www\phpunit\class\Member.php on line 14, I don't know how to make the test pass.
The problem is that dbA is not a method on ConnectFacade, but rather a variable. Therefore $connect->shouldReceive('dbA->getQuery') won't work.
Instead you need to set the dbA variable as a mock and then change the expectation to just receive getQuery:
public function testData()
{
$connect = m::mock('ConnectFacade');
$connect->dbA = $connect;
$connect->shouldReceive('getQuery')
->once()
->andReturn(true);
$actual = (new Member($connect))->data();
$this->assertTrue($actual);
}
Another option would be to instead mock the creation of the Connect class in ConnectFacade instead of mocking the whole ConnectFacade:
public function testData()
{
$connect = m::mock('overload:Connect');
$connect->shouldReceive('getQuery')
->once()
->andReturn(true);
$connectFacade = new ConnectFacade;
$actual = (new Member($connectFacade))->data();
$this->assertTrue($actual);
}
Related
I'm trying to understand how WordPress works with actions, classes, and methods.
If there is a class "TestClass" and it has a public method 'method1'
The method can be hooked to any action as "add_action('theHook', ['TestClass', 'method1']);"
From my understanding. If you don't initialize the class, you can not access its public methods and objects. Now, I would assume that WordPress has to follow this, and it must initialize my "TestClass", which will cause for public __construct() to fire.
However, after testing this, it does not fire __construct()..
Why is this?. I know a fix would be to self initialize inside 'method1', but I'm trying to figure out why WordPress behaves this way.
Because WordPress call your method as a static function: TestClass::method()
There is various solution:
1. Init class before add Action
Initialize your class before add action, like that:
$test = new TestClass();
add_action('hook', [$test, 'method']);
2. Call hook inside your Class:
class TestClass {
public function __construct() {
// Your construct
}
public function method() {
// Your Method
}
public function call_hook() {
add_action('hook', [$this, 'method']);
}
}
$test = new TestClass();
$test->call_hook();
3. Use a singleton
And if you need to to have only one instance of your class and call it in various place, you have to take a look to Singleton design pattern.
Demonstration:
class MySingletonClass {
private static $__instance = null;
private $count = 0;
private function __construct() {
// construct
}
public static function getInstance() {
if (is_null(self::$__instance)) {
self::$__instance = new MySingletonClass();
}
return self::$__instance;
}
public function method() {
$this->count += 1;
error_log("count:".$this->count);
}
}
$singleton = MySingletonClass::getInstance();
add_action('wp_head', [$singleton, 'method']);
$singleton2 = MySingletonClass::getInstance();
add_action('wp_footer', [$singleton2, 'method']);
Symfony 2.8.13 / Doctrine ORM 2.5.5 / PHPUnit 5.7.5
I want to test a method of a class that makes use of the doctrine entity manager. This public method calls a private one that instantiates a Bookmark entity, flushes it and returns this entity. Then later, in the tested method I need to access the entity Id. Everything is mocked excepted the Bookmark entity itself. The main problem is that there is no setId() method in my entity. Here is the code and my main idea to solve this issue but I don't know if it is correct ?
Tested class and method
class BookmarkManager
{
//...
public function __construct(TokenStorageInterface $tokenStorage, ObjectManager $em, Session $session)
{
//...
}
public function manage($bookmarkAction, $bookmarkId, $bookmarkEntity, $bookmarkEntityId)
{
//...
$bookmark = $this->add($bookmarkEntity, $bookmarkEntityId);
//...
$bookmarkId = $bookmark->getId();
//...
}
private function add($entity, $entityId)
{
//...
$bookmark = new Bookmark();
//...
$this->em->persist($bookmark);
$this->em->flush();
return $bookmark;
}
}
Test
class BookmarkManagerTest extends \PHPUnit_Framework_TestCase
{
public function testThatRestaurantAdditionToBookmarksIsWellManaged()
{
//...
// THIS WON'T WORK AS NO setId() METHOD EXISTS
$entityManagerMock->expects($this->once())
->method('persist')
->will($this->returnCallback(function ($bookmark) {
if ($bookmark instanceof Bookmark) {
$bookmark->setId(1);
}
}));
//...
$bookManager = new BookmarkManager($tokenStorageMock, $entityManagerMock, $sessionMock);
//...
}
}
Solutions ?
1- Make usage of reflection class as proposed here :
$entityManagerMock->expects($this->once())
->method('persist')
->will($this->returnCallback(function ($bookmark) {
if ($bookmark instanceof Bookmark) {
$class = new \ReflectionClass($bookmark);
$property = $class->getProperty('id');
$property->setAccessible(true);
$property->setValue($bookmark, 1);
//$bookmark->setId(1);
}
}));
2- Create a test Boookmark entity that extends from the real one and add a setId() method. Then create a mock of this class and replace and customize the one got from the ReturnCallback method with this one ? It seems crappy...
Any thoughts ? Thanks for your help.
The reflection looks interesting but it decreases readability of tests (mixing with mocks makes the situation tough).
I would create a fake for entity manager and implements there setting id based on reflection:
class MyEntityManager implements ObjectManager
{
private $primaryIdForPersitingObject;
public function __construct($primaryIdForPersitingObject)
{
$this->primaryIdForPersitingObject = $primaryIdForPersitingObject;
}
...
public function persist($object)
{
$reflectionClass = new ReflectionClass(get_class($object));
$idProperty = $reflectionClass->getProperty('id');
$idProperty->setAccessible(true);
$idProperty->setValue($object, $this->primaryIdForPersitingObject);
}
public function flush() { }
...
}
Once you implemented this, you can inject the instance of MyEntityManager and make your tests small and easier to maintain.
You test would look like
<?php
class BookmarkManagerTest extends \PHPUnit_Framework_TestCase
{
public function testThatRestaurantAdditionToBookmarksIsWellManaged()
{
// ...
$entityManager = MyEntityManager(1);
//...
$bookManager = new BookmarkManager($tokenStorageMock, $entityManager, $sessionMock);
//...
}
}
Of course, a situation may be harder if there is a need of setting different ids for many persisting objects. Then you can, for example, increase $primaryIdForPersitingObject on persist call
public function persist($object)
{
$reflectionClass = new ReflectionClass(get_class($object));
$idProperty = $reflectionClass->getProperty('id');
$idProperty->setAccessible(true);
$idProperty->setValue($object, $this->primaryIdForPersitingObject);
$this->primaryIdForPersitingObject++;
}
It may be extended even further to have separate primaryIdForPersitingObject each entity class, and your tests will be still clean.
I am stuck with this and I wonder what is the best way to approach this problem. I have a WebApi controller where I want to inject ICommand instance but I can know what is the instance I need once I inspect the Post request data. I'll give an example to be more clear but my question also applies to Winform events where you receive an event argument and depending of this event arg you want to have different implementation injected.
public class TestController : ApiController
{
public object Post(int id)
{
ICommand command = null;
if(id = 1)
{
command = new Id1Command();
}
else
{
command = new Id2Command();
}
return new object();
}
}
The only thing I can think of is creating a factory that accepts unity container as argument and inside that factory to call container.Resolve with named instance.
My problem with that is that I am taught that you shouldn't register or resolve outside your composition root and that is violation of the good practices (according Mark Seemann). I am looking for the best design for this problem in general.
I would use a CommandFactory and pass it to the TestController:
public class TestController : ApiController
{
private readonly ICommandFactory mCommandFactory;
public TestController(ICommandFactory CommandFactory)
{
mCommandFactory = CommandFactory;
}
public object Post(int id)
{
ICommand command = null;
if(id = 1)
{
command = CommandFactory.CreateId1Command();
}
else
{
command = CommandFactory.CreateId2Command();
}
return new object();
}
}
Now you have to make sure that Unity is creating the TestController. To do so, you have to implement, configure and set an IDependencyResolver. Check Dependency Injection in ASP.NET Web API 2.
Edit to your comment:
For this scenario you can use an autofactory using a functor that takes an int:
public class TestController : ApiController
{
private readonly Func<int, ICommand> mCommandFactory
public TestController(Func<int, ICommand> CommandFactory)
{
mCommandFactory = CommandFactory;
}
public object Post(int id)
{
var command mCommandFactory(id);
return new object();
}
}
The registration should look like this:
container.RegisterType<Func<int, ICommand>>(new InjectionFactory(
c => new Func<int, ICommand>(
id =>
{
if (id == 1)
{
return new Command();
}
else
{
return new Command2();
}
})));
Note: You still have to set the DependencyResolver!
Assume we have singleton class
class Registry {
private static $_instance;
private function __construct() {}
private function __wakeup() {}
private function __clone() {}
private $_map = array();
public static function getInstance () {
if (self::$_instance === null)
self::$_instance = new self();
return self::$_instance;
}
public function set ($key, $val) {
self::getInstance()->_map[$key] = $val;
return self::getInstance();
}
public function get($key)
{
if (array_key_exists($key, self::getInstance()->_map))
return self::getInstance()->_map[$key];
return null;
}
}
And we have simple Symfony2 Controller with 2 actions
class IndexController {
public function indexAction () {
Registry::getInstance()->set('key',true);
return new Response(200);
}
public function secondAction () {
$val = Registry::getInstance()->get('key');
return new Response(200);
}
}
I call index action, then second action. But I can't find key, that was set in first action. I think, new instance of singleton creates in my second action. Why object is not saved in memory? What do I do wrong?
If you call indexAction and secondAction in different requests it won't work the way you want it because your Registry instance is not shared between requests.
Singleton itself does not store anything "in memory" (BTW Singleton is now considered as an anti-pattern).
What, I think, you want to achieve can be done by using session storage. Check doc for more info how to implement this.
I need to mock Laravel's Eloquent\Model with Mockery and it is kind of tricky because it uses static methods.
I solved this issue with the following code but I wonder if there is a better/smarter way to do this.
<?php
use Ekrembk\Repositories\EloquentPostRepository;
class EloquentPostRepositoryTest extends TestCase {
public function __construct()
{
$this->mockEloquent = Mockery::mock('alias:Ekrembk\Post');
}
public function tearDown()
{
Mockery::close();
}
public function testTumuMethoduEloquenttenAldigiCollectioniDonduruyor()
{
$eloquentReturn = 'fake return';
$this->mockEloquent->shouldReceive('all')
->once()
->andReturn($eloquentDongu);
$repo = new EloquentPostRepository($this->mockEloquent);
$allPosts = $repo->all();
$this->assertEquals($eloquentReturn, $allPosts);
}
}
Tough to tell without the source of "Ekrembk\Repositories\EloquentPostRepository", but, I see a couple of issues. It looks like within your EloquentPostRepository, you're calling a static. You shouldn't do that. It makes it hard to test (as you've discovered). Assuming that Ekrembk\Post extends from Eloquent, you can do this instead:
<?php
namespace Ekrembk\Repositories
class EloquentPostRepository {
protected $model;
public __construct(\Ekrembk\Post $model) {
$this->model = $model;
}
public function all()
{
$query = $this->model->newQuery();
$results = $query->get();
return $results;
}
}
Then, your test will be simpler:
<?php
use Ekrembk\Repositories\EloquentPostRepository;
class EloquentPostRepositoryTest extends TestCase {
public function tearDown()
{
Mockery::close();
}
public function testTumuMethoduEloquenttenAldigiCollectioniDonduruyor()
{
$mockModel = Mockery::mock('\Ekrembk\Post');
$repo = new EloquentPostRepository($mockModel);
$eloquentReturn = 'fake return';
$mockModel->shouldReceive('newQuery')->once()->andReturn($mockQuery = m::mock(' \Illuminate\Database\Eloquent\Builder'));
$result = $mockQuery->shouldReceive('get')->once()->andReeturn($eloquentReturn);
$this->assertEquals($eloquentReturn, $result);
}
}
Didn't test the above so it might have issues, but you should get the idea.
If you look in Illuminate\Database\Eloquent\Model, you'll see that "public static function all" is really just calling "get" on an instantiated eloquent model.