I'm using reflection to test if a class has a particular parent class and then returning an instance of it.
if (class_exists($classname)) {
$cmd_class = new \ReflectionClass($classname);
if($cmd_class->isSubClassOf(self::$base_cmd)) {
return $cmd_class->newInstance();
}
}
I want to be able to unit test these instantiated objects but I don't know if there is any way to use dependency injection in this case. My thought was to use factories to get dependencies. Is the factory pattern the best option?
My thought was to use factories to get dependencies
By using this method you still would not know which dependencies a class has.
I would recommend going a step further, and resolve the dependencies with the Reflection API, too.
You can actually type hint constructor arguments and the Reflection API is fully capable of reading the type hints.
Here is a very basic example:
<?php
class Email {
public function send()
{
echo "Sending E-Mail";
}
}
class ClassWithDependency {
protected $email;
public function __construct( Email $email )
{
$this->email = $email;
}
public function sendEmail()
{
$this->email->send();
}
}
$class = new \ReflectionClass('ClassWithDependency');
// Let's get all the constructor parameters
$reflectionParameters = $class->getConstructor()->getParameters();
$dependencies = [];
foreach( $reflectionParameters AS $param )
{
// We instantiate the dependent class
// and push it to the $dependencies array
$dependencies[] = $param->getClass()->newInstance();
}
// The last step is to construct our base object
// and pass in the dependencies array
$instance = $class->newInstanceArgs($dependencies);
$instance->sendEmail(); //Output: Sending E-Mail
Of course you need to do error checking by yourself (If there's no Typehint for a constructor argument for example). Also this example does not handle nested dependencies. But you should get the basic idea.
Thinking this a step further you could even assemble a small DI-Container where you configure which instances to inject for certain Type Hints.
Related
I am testing a class, let's call it ClassUnderTest using another class, let's call it OtherClass. In my Test I do:
$OtherClassStub = $this->createStub(OtherClass::class);
$OtherClassStub->method(...)
->willReturn(...);
$ClassUnderTest->otherClass = $OtherClassStub;
That works. But when the $ClassUnderTest calls new OtherClass(), the original OtherClass class is created instead of the stub.
How can I achieve that every possible instance of OtherClass in the context of the test is replaced by the stub?
From your description I infer that in principle you have something like this:
class OtherClass {
protected function someMethod(): bool
{
// determine $x ...
return $x;
}
}
class ClassUnderTest {
public OtherClass $otherClass;
public function methodToBeTested(): bool
{
$otherClass = new OtherClass();
return $otherClass->someMethod();
}
}
class ClassUnderTestTest extends TestCase {
public function testMethodToBeTested(): void
{
$otherClassStub = $this->createStub(OtherClass::class);
$otherClassStub->method('someMethod')
->willReturn(true);
$classUnderTest = new ClassUnderTest();
$classUnderTest->otherClass = $otherClassStub;
$result = $classUnderTest->methodToBeTested();
$this->assertTrue($result);
}
}
Now the assertion in your test may hold or it may fail. Why? Because you are not calling the method you stubbed on the $otherClassStub. Instead you instantiate a new $otherClass object in the method you're testing (or somewhere down the line).
Either your ClassUnderTest should always use the OtherClass object from the ClassUndertTest::otherClass attribute (assuming that's why you put it there in the first place).
Or you could use some other form of dependency injection, e.g. by using a framework like Symfony or Laravel. (In the case of Symfony you can even use only the DependencyInjection Component, no idea if that's possible with Laravel, too.)
The simple answer to your actual question is: you cannot change the behaviour of the new keyword. Calling new on a class will always instantiate a new object based on exactly that class, unless the constructor of that class defines something else.
(You might want to get the concept of classes and objects straight, your code example as well as your question seem to indicate that you're not quite clear on that. Maybe reading up on that as well as on the concept of dependency injection will help you.)
Perhaps a solution to your problem is presented here:
How to Build a PHP Plugin Module System
This is one way to load classes as plugins and they can be called from each other. With modifying this system a bit, you can create as many "new OtherClass()" as you like from your code and still access everything from other classes. If you want multiple instances of a class, perhaps modify it into this direction:
function load ($module,$instance) {
if (isset($this->$module->$instance)) { return true; }
From above link:
<?php
class Core {
// (A) PROPERTIES
public $error = ""; // LAST ERROR MESSAGE
public $pdo = null; // DATABASE CONNECTION
public $stmt = null; // SQL STATEMENT
public $lastID = null; // LAST INSERT/UPDATE ID
// (B) LOAD SPECIFIED MODULE
// $module : module to load
function load ($module) {
// (B1) CHECK IF MODULE IS ALREADY LOADED
if (isset($this->$module)) { return true; }
// (B2) EXTEND MODULE ON CORE OBJECT
$file = PATH_LIB . "LIB-$module.php";
if (file_exists($file)) {
require $file;
$this->$module = new $module();
// EVIL POINTER - ALLOW OBJECTS TO ACCESS EACH OTHER
$this->$module->core =& $this;
$this->$module->error =& $this->error;
$this->$module->pdo =& $this->pdo;
$this->$module->stmt =& $this->stmt;
return true;
} else {
$this->error = "$file not found!";
return false;
}
}
}
ps. thank you for the mod, who made me work a bit more to keep this answer online. the answer is so much better now.
I'm writing some unit-tests for a typo3 v9.5 extension, and I can not figure how to properly mock objects when the under-test function is instanciating them using GeneralUtility::makeInstance(ObjectManager::class)->get(...)
If possible, I'd like to use the prophecy framework, but that's not mandatory.
For example, if the function under-test is like:
public function getRootline($pageUid) {
$pageService = GeneralUtility::makeInstance(ObjectManager::class)->get(PageService::class);
return $pageService->getRootLine($pageUid);
}
How could I test this and mock the Pageservice class?
I tried different variations of this solution:
$pageServiceProphecy = $this->prophesize(PageService::class);
$pageServiceProphecy->getRootLine($pageUid)->shouldBeCalled()->willReturn($myRootLine);
$omProphecy = $this->prophesize(ObjectManager::class);
$omProphecy->get(PageService::class)->shouldBeCalled()->willReturn($pageServiceProphecy);
GeneralUtility::setSingletonInstance(ObjectManager::class, $omProphecy->reveal());
But I get various cryptic errors every time.
The given version raises the error:
TYPO3\CMS\Core\Cache\Exception\NoSuchCacheException: A cache with identifier "cache_runtime" does not exist.
Actually you should make use of dependency injection in the class which exposes the getRootline() API, then you can have the PageService injected this way:
final class MyCustomClass {
private PageService $pageService;
public function __construct(PageService $pageService)
{
$this->pageService = $pageService;
}
public function getRootline(int $pageUid)
{
return $this->pageService->getRootLine($pageUid);
}
}
While testing you can inject a mock instead:
$pageService = $this->prophesize(PageService::class);
$myCustomObject = new MyCustomClass($pageService->reveal());
I would like to test some of my services, but I can not find any example on Laravel's website:
https://laravel.com/docs/5.1/testing
They show how to test simple classes, entities, controllers, but I have no idea how to test services. How is it possible to instantiate a service with complex dependencies?
Example service:
<?php
namespace App\Services;
// Dependencies
use App\Services\FooService;
use App\Services\BarService;
class DemoService {
private $foo_srv;
private $bar_srv;
function __construct(
FooService $foo_srv,
BarService $bar_srv
) {
$this->foo_srv = $foo_srv;
$this->bar_srv = $bar_srv;
}
// I would like to test these two functions
public function demoFunctionOne() {
// ...
}
public function demoFunctionTwo() {
// ...
}
}
The quickest thought that might come to the mind is to create a copy of those Service classes, but that could grow so big. This is why there's MockObject in PhpUnit.
To achieve this mock and use it as a replacement to the service class you may need to resolve it in Laravel service container.
Here is how it will look:
class DemoServiceTest extends TestCase
{
// Dependencies
use App\Services\FooService;
use App\Services\BarService;
use App\Services\DemoService;
public function testDemoFunctionOne()
{
$foo_srv = $this->getMockBuilder(FooService::class)
//->setMethods(['...']) // array of methods to set initially or they return null
->disableOriginalConstructor() //disable __construct
->getMock();
/**
* Set methods and value to return
**/
// $foo_srv->expects($this->any())
// ->method('myMethod') //method needed by DemoService?
// ->will($this->returnValue('some value')); // return value expected
$bar_srv = $this->getMockBuilder(BarService::class)
// ->setMethods(['...']) // array of methods to set initially or they return null
->disableOriginalConstructor()
->getMock();
/**
* Set methods and value to return
**/
// $bar_srv->expects($this->any())
// ->method('myMethod') //method needed by DemoService?
// ->will($this->returnValue('some value')); // return value expected
$demo_service = new DemoService($foo_srv, $bar_srv);
$result = $demo_service->demoFunctionOne(); //run demo function
$this->assertNotEmpty($result); //an assertion
}
}
We are creating new mock for both FooService and BarService classes, and then passing it when instantiating DemoService.
As you can see without uncommenting the commented chunk of code, then we set a return value, otherwise when setMethods is not used, all methods are default to return null.
Let's say you want to resolve these classes in Laravel Service Container for example then you can after creating the mocks, call:
$this->app->instance(FooService::class, $foo_srv);
$this->app->instance(BarService::class, $bar_srv);
Laravel resolves both classes, feeding the mock classes to any caller of the classes So that you just call your class this way:
$demoService = $this->app->make(DemoService::class);
There are lots of things to watch out for when mocking classes for tests, see sources: https://matthiasnoback.nl/2014/07/test-doubles/, https://phpunit.de/manual/6.5/en/test-doubles.html
I have an example using the service you gave below. The basic idea is that for dependencies you just assume that they'll work as expected and create mocks for them.
Mocks are special classes that pretend to be a different class, but don't really do anything unless you tell them to. For example, say we have a class called UserCreationService that takes a few arguments required to create a new user. This class depends on some things like a Mailer class to send a registration mail, and a UserRepository class to save the user to the database. It also does a lot of validation of the user, and we'd like to check lots and lots of edge cases for all the different possible arguments to create a user.
In this example do we really want to check that the user was saved to the database? Do we want to check for sure that the mail was really sent? We could do that, but it would take a very long time to run all our test cases. Instead we just assume that the classes our UserCreationService depends on will just do their job and we create mock classes for the dependencies. We would create mocks for the Mailer and UserRepository and just tell the test that we expect some methods to be called (like sendRegistrationMail for the Mailer) and concentrate on the logic contained in our class.
// This is the class we want to test
class UserCreationService {
// The dependencies
private $userRepository;
private $mailer;
public function __construct($userRepository, $mailer)
{
$this->userRepository = $userRepository;
$this->mailer = $mailer;
}
public function create($name, $email, $location, $age)
{
$user = new User();
// do some complex validation here. This is what we want to test
$this->validateName($name);
$this->validateEmail($email);
$this->validateLocation($location);
$this->validateAge($age);
// then call our external services
$this->userRepository->save($user);
$this->mailer->sendRegistrationMail($user);
}
}
// This is a sample test for the above class using mocking
class UserCreationServiceTest extends TestCase
{
public function testValidUserWillBeSaved() {
// The framework allows using argument tokens.
// This just means an instance of *any* user class is expected
$anyUserToken = Argument::type(User::class);
// The prophesize method creates our mock for us
// We then define its behavior
$mailer = $this->prophesize(Mailer::class);
// Let our test know we expect this method to be called
// And that we expect an instance of a user class to be passed to it
$mailer->sendRegistrationMail($anyUserToken)->shouldBeCalled();
$userRepo = $this->prophesize(UserRepository::class);
$userRepo->save($anyUserToken)->shouldBeCalled();
// Create our service with the mocked dependencies
$service = new UserCreationService(
$userRepo->reveal(), $mailer->reveal()
);
// Try calling our method as part of the test
$result = $service->create('Tom', 'tom#test.com', 'Ireland', 24);
// Do some check to see that the result we got is what we expected
$this->assertEquals('Tom', $result->getName());
}
}
This is something similar, but specific to the example you gave above:
use PHPUnit\Framework\TestCase;
class DemoServiceTest extends TestCase
{
public function testDemoFunctionOne()
{
// These are the variables that will be passed around the service
$sampleId = 1;
$myModel = new MyModelClass();
// Set up a mock FooService instance
$fooMock = $this->prophesize(FooService::class);
// Tell the test that we expect "findFoo" to be called and what to return
$fooMock->findFoo($sampleId)->willReturn($myModel);
// Set up a mock BarService instance
$barMock = $this->prophesize(BarService::class);
// Tell the test we expect "save" to be called and what argument to expect
$barMock->save($myModel)->shouldBeCalled();
// Create an instance of the service you want to test with the mocks
$demoService = new DemoService($fooMock->reveal(), $barMock->reveal());
// Call your method, get a result
$result = $demoService->demoFunctionOne($sampleId);
// Check that the result is what you want
$this->assertEquals($myModel, $result);
}
}
I'd have a look at Laravel specific stuff here and prophecy here
i am not sure about the context you have there, but I will try to have an answer for you based on an example.
Imagine that you want to test a payment gateway from a payment provider.
My approach is to make 2 payment gateways extend something like this interface:
<?php
namespace App\Payment;
interface PaymentGateway
{
public function charge($amount, $token);
public function getTestToken();
....
}
then i will create the real payment gateway that is used in the controllers or where ever you need it and for the tests the 'fake' payment and this is an exact copy of the real one but with dummy data. This you can use on the tests because it is faster and it is a 1to1 copy of the real. I think if the service it self works or not via the internet it is outside of the testing scope, at least for now. So you will end up with something like this:
<?php
namespace App\Payment;
class FakePaymentGateway implements PaymentGateway
{
private $tokens;
const TEST_CARD_NUMBER = '1234123412341234';
public function __construct()
{
$this->tokens = collect();
}
public function getTestToken()
{
return 'fake-tok_'.str_random(15);
}
....
}
and the real one:
<?php
namespace App\Payment;
class PaypalPaymentGateway implements PaymentGateway
{
...
public function __construct(PayPal $PaypalClient)
{
...
}
public function charge($amount, $token)
{
...
}
....
}
so i think in your case, when you have complex dependencies, you have to fake all of that, depending on the case, into the fake service.
the tests will look like this for the fake service:
<?php namespace Tests\Unit\Payment;
use App\Payment\FakePaymentGateway;
use Tests\TestCase;
class FakePaymentGatewayTest extends TestCase
{
use PaymentGatewayContractTests;
protected function getPaymentGateway()
{
return new FakePaymentGateway;
}
...
}
for the real one like this:
<?php namespace Tests\Unit\Payment;
use App\Payment\PaypalPaymentGateway;
use Tests\TestCase;
/**
* #group integration
*
* ./vendor/phpunit/phpunit/phpunit --exclude-group integration
*/
class PaypalPaymentGatewayTest extends TestCase
{
use PaymentGatewayContractTests;
protected function getPaymentGateway()
{
return new PaypalPaymentGateway(....);
}
...
}
for the real one you should ignore it in the phpunit when running to make the tests faster and not depending of the internet connection and so on. It is also nice to have in the tests suite, when changes from the service occur.
You will end up having a better understanding of the dependencies and maybe you will also do some refactoring. Anyway i guess it is a lot to work but when the real implemanation will change you can see that also from the tests and change it faster.
I hope my answer will help you in your service tests :).
Maybe you can use Mockery to mock the Dependencies.
We are doing this for our cases and it does the work, yet. :)
Especially the partial Mock is doing fine here.
https://laravel.com/docs/5.8/mocking#mocking-objects
In an application I'm building there's a CLI entry point class:
class CLIEntryPoint {
protected $factory;
public function __construct(ApplicationObjectFactoryInterface $factory) {
$this->factory = $factory;
}
public function run(...$args) {
$choice = $args[1];
$appObject = $this->factory->makeApplicationObject($choice);
$appObject->doApplicationRelatedStuff();
}
}
This entry point is created using Dependency Injection in my "front controller" script and it receives an ApplicationObjectFactoryInterface implementation (actually the current implementation of ApplicationObjectFactoryInterface is injected by the DI container, which in turn reads it from its configuration file, but that's not the point).
The current implementation of ApplicationObjectFactoryInterface also uses DI and depends on other factories which help it building the resulting application object:
class CurrentImplementationOfApplicationObjectFactory implements ApplicationObjectFactoryInterface {
protected $someComponentFactory;
protected $anotherComponentFactory;
public function __construct(SomeComponentFactoryInterface $someComponentFactory, AnotherComponentFactoryInterface $anotherComponentFactory) {
$this->someComponentFactory = $someComponentFactory;
$this->anotherComponentFactory = $anotherComponentFactory;
}
/**
* Interface's method
*
* #return ApplicationObjectInterface
*/
public function makeApplicationObject($choice) {
$component = $this->someComponentFactory->makeSomeComponent();
$anotherComponent = $this->anotherComponent->makeAnotherComponent();
switch ($choice) {
case 1:
return new CurrentImplementationOfApplicationObject1($component, $anotherComponent);
case 2:
return new CurrentImplementationOfApplicationObject2($component, $anotherComponent);
default:
return new DefaultImplementationOfApplicationObject($component, $anotherComponent);
}
}
}
Here CurrentImplementationOfApplicationObject1, CurrentImplementationOfApplicationObject2 and DefaultImplementationOfApplicationObject all implement the ApplicationObjectInterface interface and therefore they all have the doApplicationRelatedStuff method.
I would like to know whether it's good practice or not to write code like I did and if not how can I improve it.
Basically here I am creating a component which depends on other components in order to function properly using a factory which in turn needs inner factories to build the component which implements the ApplicationObjectInterface interface.
Is it considered good practice?
Thanks for the attention, as always!
EDIT: I looked at the article of Steven and tried to refactor CLIEntryPoint. The only problem now seems to be how to pass the $choice parameter to the factory which now is inside of the proxy when the run() method is called. Is this code structure better than the one I posted above? Of course, SomeComponentFactoryInterface and AnotherComponentFactoryInterface should follow the same behaviour (the factory that uses them should not use them directly, but through two proxies which implement, in order, SomeComponentInterface and AnotherComponentInterface). I hope I get it right, anyway, here is the code:
class CLIEntryPoint {
protected $applicationObject;
public function __construct(ApplicationObjectInterface $applicationObject) {
$this->applicationObject = $applicationObject;
}
public function run(...$args) {
$choice = $args[1]; // How do I deal with different choices when I am using a Proxy? I should have different application objects depending on input.
$this->applicationObject->doApplicationRelatedStuff();
}
}
interface ApplicationObjectInterface {
public function doApplicationRelatedStuff();
}
class ApplicationObjectProxy implements ApplicationObjectInterface {
protected $applicationObjectFactory;
protected $applicationObjectImplementation = NULL;
public function __construct(ApplicationObjectFactoryInterface $factory) {
$this->applicationObjectFactory = $factory;
}
public function __call($method, $args) {
// Calling interface's
$implementation = $this->getImplementation();
$methodOfInterfaceToCall = preg_replace('/Proxy$/', '', $method);
return $implementation->{$methodOfInterfaceToCall}(...$args);
}
/**
* Laxy loading method.
*/
protected function getImplementation() {
if (is_null($this->applicationObjectImplementation)) {
$this->applicationObjectImplementation = $this->applicationObjectFactory->makeApplicationObject(); // Choice should go here somehow...
}
return $this->applicationObjectImplementation;
}
public function doApplicationRelatedStuff() {
// This will call the PHP's magic `__call` method, which in turn will forward the call to the application object's
// implementation returned by the factory.
return $this->doApplicationRelatedStuffProxy();
}
}
Actually yes, this is a pattern called the Abstract Factory Pattern. So an example that I used to present it in front of my class during my undergrad:
So if you are building a video game first person shooter, you might want to create three concrete factories like:
FlyingMonsterFactory
SwimmingMonsterFactory
WalkingMonsterFactory.
All these factories would implement an abstract MonsterFactory.
With this, you can have your video game create a level in which you want waves of the same type of monsters, so you can have a randomWaveMonsterGenerator method return a MonsterFactory which might have returned a concrete SwimmingMonsterFactory. So then you will have a wave of SwimmingMonster(s) generated by the SwimmingMonsterFactory.
So answer your description more directly, looking at your code above, you asked the question on choice for Dependency Injection. With Dependency Injection, I believe for this type of pattern, you will have to inject every concrete class before your code even attempts to get the implementation class.
So for example:
Your code above says the run method gives an argument called
choice.
With this choice, you will have to use it as a parameter into a getImplementation method.
All the concrete objects that the getImplementation method that rely upon Dependency
Injection have to be created BEFORE you call the getImplementation method.
But since you don't know which implementation class will be called, I believe you have to inject ALL the implementation classes before hand.
Then you can use the choice variable as a parameter to get the correct implemented factory class.
Hope this helps!
public function getHelperInstance()
{
$user = new Helper();
$user->set($result['data']);
return $user;
}
I am calling getHelper() class multiple times and if $user is not empty than am calling getHelperInstance(), now in my case getHelperInstance() always creates a new instance of Helper() class and so every time I call getHelperInstance() function am creating a new instance of Helper() so is there any way where can I can just create one instance of Helper() and use it multiple times instead of creating a new instance everytime. Any suggestions !!!
public function getHelper()
{
$user = array();
if (!empty($user))
{
$user = $this->getHelperInstance();
}
return $user;
}
Here is what Erich Gamma, one of the Singleton pattern's inventors, has to say about it:
"I'm in favor of dropping Singleton. Its use is almost always a design smell"
So, instead of a Singleton, I suggest to use Dependency Injection.
Create the Helper instance before you create what is $this. Then set the helper instance to the $this instance from the outside, either through a setter method or through the constructor.
As an alternative, create a Helper broker that knows how to instantiate helpers by name and pass that to the $this instance:
class HelperBroker
{
protected $helpers = array();
public function getHelper($name)
{
// check if we have a helper of this name already
if(!array_key_exists($name, $this->helpers)) {
// create helper and store for later subsequent calls
$this->helpers[$name] = new $name;
}
return $this->helpers[$name];
}
}
This way you can lazy load helpers as needed and will never get a second instance, without having to use Singleton. Pass an instance of the broker to every class that needs to use helpers.
Example with a single helper
$helper = new Helper;
$someClass = new Something($helper);
and
class Something
{
protected $helper;
public function __construct($helper)
{
$this->helper = $helper;
}
public function useHelper()
{
$return = $this->helper->doSomethingHelpful();
}
}
Inside $something you can now store and access the helper instance directly. You don't need to instantiate anything. In fact, $something doesn't even have to bother about how a helper is instantiated, because we give $something everything it might need upfront.
Now, if you want to use more than one helper in $someClass, you'd use the same principle:
$helper1 = new Helper;
$helper2 = new OtherHelper;
$something = new Something($helper1, $helper2);
This list will get rather long the more dependencies you insert upfront. We might not want to instantiate all helpers all the time as well. That's where the HelperBroker comes into play. Instead of passing every helper as a ready instance to the $something, we inject an object that knows how to create helpers and also keeps track of them.
$broker = new HelperBroker;
$something = new Something($broker);
and
class Something
{
protected $helperBroker;
public function __construct($broker)
{
$this->helperBroker = $broker;
}
public function doSomethingHelpful()
{
$return = $this->getHelper('foo')->doSomethingHelpful();
}
public function doSomethingElse()
{
$return = $this->getHelper('bar')->doSomethingElse();
}
}
Now $something can get the helpers it needs, when it needs them from the broker. In addition, any class that needs to access helpers does now no longer need to bother about how to create the helper, because this logic is encapsulated inside the broker.
$broker = new HelperBroker;
$something = new Something($broker);
$other = new Other($broker);
The broker also makes sure that you only have one helper instance, because when a helper was instantiated, it is stored inside the broker and returned on subsequent calls. This solves your initial problem, that you don't want to reinstance any helpers. It also doesn't force your helpers to know anything about how to manage themselves in the global state, like the Singleton does. Instead you helpers can concentrate on their responsibility: helping. That's clean, simple and reusable.
It sounds like you are interested in the singleton pattern. If you are using PHP5+, you should be able to take advantage of PHP's OOP stuff.
Here's an article on how to implement a singleton in php4. (But I would strongly suggest updating to php5 if that is an option at all)
class Singleton {
function Singleton() {
// Perform object initialization here.
}
function &getInstance() {
static $instance = null;
if (null === $instance) {
$instance = new Singleton();
}
return $instance;
}
}
PHP 4 Singleton Pattern
FYI, if you have any control over which PHP version you use you really should migrate to PHP 5.