How to provide a mock entity manager to test a service class? - php

I have a service class (OutletScraper) in my Symfony (3.4) application. The class uses Entity Manager and a Geocoding service provided by the Bazinga Geocoder bundle. I've configured both successfully so that I am able to call them from within my service class. Whenever I need the service, I am calling it from the container so the entity manager and geocoder bundle is injected into it already.
When testing, I understand that I can mock the entity manager and then provide this to my test class. As I am accessing the service class from the container, how do I override what gets passed to the constructor? ie so that I can provide the mock entity manager instead of it being injected with the real one. I tried to instantiate an object of the service class manually:
$outletScraper = new OutletScraper(new Provider(), $this->createMock(EntityManagerInterface::class));
However I get the following error when doing so:
Error: Cannot instantiate interface Geocoder\Provider\Provider
How can I instantiate this class correctly? Do I need to call it from the service container (its set to private)? Appreciate any help.

Your mock of the entity manager works fine, the problem is the geocoder provider. Just like EntityManagerInterface, Geocoder\Provider\Provider is also an interface. The library maintainers just chose to omit the suffix.
That means you can't just create it, but instead have to pass a concrete class implementing the interface, like Geocoder\Provider\GoogleMaps\GoogleMaps, if you actually want to do the geocoding call or mock the Provider as well.
If you want to check if your configured geocoding provider works you can write a functional test using Symfony's WebTestCase, that looks roughly something like this:
<?php
namespace AppBundle\Tests\Scraper;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class OutletScraperTest extends WebTestCase
{
public function testFindsLocation()
{
// This will instantiate your Symfony application in the test environment
$client = static::createClient();
$container = $client->getContainer();
// Replace the class name with the service id, if you use artificial service ids like "app.outlet_scraper"
$scraper = $container->get(OutletScraper::class);
// Call whatever method you want to test on your outlet scraper
$result = $scraper->someMethod();
// Assert result matches expectations
$this->assertEquals(..., $result);
}
}
Be careful though, that this will use the OutletScraper you configured for your application, with both actual dependencies. So your geocode provider will make an actual call to whatever provider you use, which might use up requests, which might be limited. Also you will use the real Doctrine EntityManager, meaning whatever you write to the database will actually be written. Especially for the database you should therefore create a separate test database and configure it in your app/config/config_test.yaml.

My recommendation would be that if you are trying to test this service alone, you should also mock the Geocoder\Provider\Provider. Otherwise, your test assertions could bring up different results depending on when you run them.
If you are going to use this mock a lot, you could even create your own testing class that implements that interface, so that you can prepare the different outputs yourself on your test environment.

Related

How to write and use a PHP Dependency Injection Conatiner using PHP Reflection API

I'm trying to implement a simple DI in a pure OOP application. I wanted to use Dependency injection to manage many services ( Database, RequestValidator, Cache etc. ). I have read many blogs and liked this one from tech-tajawal but I could not really understand where should I include the container that tech-tajawal wrote. Can someone please show me how to do it?
I want it clean and thus want to use constructor based injection. So If I have a class, let's say AbstractBaseController which will inject a dependency called Request, so I will write:
php:
<?php
namespace src\controllers;
use system\middlewares\Request as Request;
abstract class AbstractBaseController {
private $request;
public function __construct(Request $request) {
$this->request = $request;
return $this;
}
}
But this simply throws
Fatal error: Uncaught TypeError: Argument 1 passed to src\controllers\AbstractBaseController::__construct() must be an instance of system\middlewares\Request, none given`
I think the container from tech-tajawal has to be included in my project root someway but I don't know how.
Please excuse my naivety here as I always was framework dependent.
You should instantiate your container at the very beggining of your application (think of a bootstrap class, or even at the top of index.php itself, considering a very simplistic application), because you will need the container to be ready before all the subsequent instantiations of services.
The only other thing that could probably be executed before the container instantiation are those related to configuration, because those are usually needed for the container to work properly (configuration parameters, PSR-4 autoloading configuration, etc).
For example, suppose that you have a class called MyController that extends the abstract class AbstractBaseController.
Then, on index.php, for example, you could instantiate your container and your controller:
//index.php
$container = new Container();
$controller = $container->get('namespace\of\MyController');
$controller->render();
When you do that, all the dependencies from the constructor would be handled by the autowiring module of your container library.
In a real life application, the instantiation of a controller would be usually handled inside a router, which maps URL addresses, methods and parameters to different classes to be loaded by the container.
One rule of thumb with autowiring, is that you can never call new namespace\of\MyController() directly anymore, because instantiating it manually would require you to pass each of the constructor dependencies (so you are not really using the autowiring feature). The right way to instantiate it is always by using $container->get('namespace\of\MyController').

How to mock doctrine service for controller action when doctrine is used elsewhere

I have a controller action that contains some code like the following:
$repository = $this->get("doctrine")->getRepository(User::class);
$user = $repository->findOneBy(array('username' => $request->request->get("username")));
I wanted to mock the repository. At first I wasn't sure how to do that, but then I found this SO post: Testing Controllers in Symfony2 with Doctrine
From the answer there, I surmised that I should create a mock of the doctrine service AND the repository object, and tell the mockbuilder that the repository object returns the entity I want to test. Then I should replace the doctrine service using the following line of code:
$client->getContainer()->set("doctrine", $doctrineMockObject);
and then make the request:
$client->request("POST", "/checkUsername");
The problem with this is that there is a twig template that actually calls a separate controller action, and that action uses doctrine as well. So that causes the application to break since it is using the mock doctrine object I injected into the container.
Is there any way to only use the mock doctrine service for the action that I am testing? Otherwise, is there any alternative method to do what I want to do? I am out of ideas.
As 'alternative method' you can stop using Container as Service Locator and instead of injecting it everywhere inject only services that you need into controllers/other services.

ServiceLocator, let's thoughts about it in ZF2 context

According to Marco's Pivetta thoughts with this, this old question
and my answer to an other question
I was interrogating myself about the better way to use our Services in Zend Framework 2 application.
Actually we can use the ServiceLocatorAwareInterface combined with ServiceLocatorAwareTrait.
With the fact In ZF3 service locator will be removed in controller It may be possible that they will also remove this interface, or advice people not using it, it make sense.
The only way I see how our Services may be constructed is :
Don't use ServiceLocator in your Services, use DependancyInjection.
The problem is :
Some project are just so big that you have either :
15 services's class for one workflow.
A service's class with 15 Dependancies.
Pick your nightmare...
Some example for what you may need in a service :
Get back the formManager (you can't call it in the controller)
You may need to get your ViewRenderer to render template before returning an HTML string to the view through AJAX, and JSON response;
You may need to get back the translator or every service you want provided by ZF2
Get your entity Manager, if you have multiple database, add count here
Get others service like MailService, ExportService, ImportService and so on...
If you have to load specifics services depends on a client (multi-client website in BtoB... add somes services, because you can't load | call an AbstractFactory)
Maybe for some of those points, they're can be solved by a tricks that I don't know.
My Question is :
Is it a good practise to have 15 or more Dependancies for one service
and give up the ServiceLocator, in controllers, but also in services ?
Edit from comments
For illustrate my point, I paste one of my constructor :
public function __construct(
ToolboxService $toolboxService,
EntityService $entityService,
UserService $userService,
ItemService $itemService,
CriteriaService $criteriaService,
Import $import,
Export $export,
PhpRenderer $renderer
) {
$this->toolboxService = $toolboxService;
$this->entityService = $entityService;
$this->userService = $userService;
$this->emOld = $this->toolboxService->getEmOld();
$this->emNew = $this->toolboxService->getEmNew();
$this->serviceLocator = $this->toolboxService->getServiceLocator();
$this->itemService = $itemService;
$this->criteriaService = $criteriaService;
$this->import = $import;
$this->export = $export;
$this->renderer = $renderer;
$this->formManager = $this->toolboxService->getFormManager();
}
As you can see, ToolboxService is an object with multiple dependancies itself. This Service is in my Application folder, and almost everywhere.
I have 2 entity Managers (connection to 2 databases, but maybe soon, i will need a third one...)
You can see that I use the serviceLocator throught a dependancy, so this service doesn't implements ServiceLocatorAwareInterface. If I'm not using it, i'm literraly screwed for my AbstractFactory call with
// Distribute somes orders depends on Clients
$distributionClass = $this->serviceLocator->get(ucfirst($param->type));
if ($distributionClass instanceof DistributeInterface) {
$distributionClass->distribute($orders, $key);
} else {
throw new \RuntimeException("invalid_type_provided", 1);
}
Let's say you would inject the ServiceLocator instance. There is no guarantee that the ServiceLocator actually holds your hard dependencies, thus breaking the DI pattern. When using constructor dependency injection you are sure that all the services that are needed are really available. If not, the constructing of the service will simply fail.
When using a ServiceLocator you will end up in an instantiated service class where hard dependencies might or might not be available through the ServiceLocator. This means you have to write all kind of additional logic (check dependencies, throw exceptions) in case the dependency cannot be resolved from the ServiceLocator instance the moment you ask for it. Writing all this code will probably be much more work then injecting 15 dependencies and on top of that the logic will be cluttered all over your service.
Additionally you would still need to add all the setter and getter methods to be able to get your services from your ServiceLocator and to make your service testable.
IMHO injecting 15 dependencies is less code and easier to maintain then injecting a ServiceLocator instance.

PhalconPHP DI : Initializers

I am currently testing phalcon php for a project, and I am looking for a way to automatically inject certain classes automatically based on an implemented interface.
The Dependency Injection reference has an example where if a class implements Phalcon\DI\InjectionAwareInterface, it will automatically inject the DI into that class.
What I want to do is similar. If a class has for instance Aranea\Db\DbAdapterAware, it should automatically inject the DbAdapter in that class. I am looking for something similar to what Zend Framework 2 does (https://juriansluiman.nl/article/121/interface-injection-with-initializers-in-zend-servicemanager), where during DI config you can specify initializers like this:
'initializers' => array(
'logger' => function($service, $sm) {
if ($service instanceof LoggerAwareInterface) {
$logger = $sm->get('logger');
$service->setLogger($logger);
}
}
),
If this is not automatically possible in PhalconPHP, I was thinking of overriding the FactoryDefault class and implement it myself. What would be the right place to inject this logic? In the get* methods, or rather in the set* methods? I assume that a method is not initialized during DI initializing but on first call, so get* would sounds more appropriate?
Thanks for your advice,
Jeroen
The Dependency Injection reference has an example where if a class implements Phalcon\DI\InjectionAwareInterface, it will automatically inject the DI into that class.
That is not entirely true, what it means is that the DI gets (automatically) injected when the service is resolved given it implements this interface, the DI doesn't magically appears there just because the class implements some interface.
If a class has for instance Aranea\Db\DbAdapterAware, it should automatically inject the DbAdapter in that class.
That is sort of how it works (not technically) if your class extends the Phalcon\DI\Injectable (or implements the InjectionAwareInterface in the same way as Phalcon\DI\Injectable). Inside Injectable there is a __get magic, which returns the service from the DI if the service exists. In other words stuff get injected only in the DI, and other classes lookup for services in there.
To inject your own services you can either pass them in your configuration to the DI or extend the DI or FactoryDefault. The difference between the two is that FactoryDefault already comes preconfigured with the useful services, which you might not need though.
I assume that a method is not initialized during DI initializing but on first call, so get* would sounds more appropriate?
Yes, there is a Phalcon\DI\Service object that represents the service and resolved when called for the first time (if it's a shared service) or resolved every time (if it's not). You normally would want all your services to be shared, otherwise this often becomes a bottleneck, e.g., when resolving a non-shared database adapter, which establishes the connection every time you call it…
PS: Note, for it to work as you want it with the DbAdapter you can do a few things:
Add the adapter getter and return DI::getDefault()->getShared('db');
Extend the Phalcon\DI\Injectable and set the DI when the class is created, so it can lookup for services.
Every time you need the adapter simply get it from the DI like shown in the first option.

Adapters and Dependency Injection

I am currently building an MVC application in PHP (not using any frameworks). I am using yadif (https://github.com/beberlei/yadif) for dependency injection.
I would like to build a login module. It should be able to use adapters, for example one might be able to set that logins are authenticated using the MySql database or some LDAP directory. This setting would be done in the admin area and is stored in a database.
I imagine that I would have an abstract adapter:
<?php
abstract AbstractLoginAdapter{
abstract function login($username, $pass){}
}
I would then just implement adapters like so:
<?php
MySQLLoginAdapter extends AbstractLoginAdapter{
public function login($username, $pass){
//do stuff
}
}
That's all nice and well, but how do I create an instance of the adapter? Usually, dependencies would be injected using yadif via the constructor:
<?php
class loginController{
private $_adapter;
public function __construct(AbstractLoginAdapter $adapter){
$this->_adapter = $adapter;
}
}
However, since I don't know which concrete adapter will be injected, I can't set that in a configuration before hand. Yadif allows me to create a configuration which I then need to pass to the container:
$builder = new Yadif_Builder();
$builder->bind("loginController")
->to("loginController")
->args($SelectedLoginAdapter);
Since the application uses a front controller, a DI container is created there. It then creates a routing object etc.
In light of this, should I pass a reference of that container down to the loginController object, and then use that container to instantiate my adapter?
Or should I instantiate a new container within my loginController object and then just load in an instance of the adapter?
I would do the first: pass a reference down to your controller. You'll want to use a single Dependency Injector Container (DIC) in your application. You don't want to create a new DIC whenever you need access to it. That would lead to duplication of objects stored in the DIC.
I know this is how Symfony 2 does it. All controllers (and many other classes) implement the ContainerAware interface. That interface has a single method setContainer() that is used to pass down a reference to the DIC.
I don't know about your specific DI tool but from a DI point of view you would be specifying which type to use. The container itself is responsible for instantiating a new instance (and possibly of all the dependencies of that type as well) of the configured type.
The benefit of DI in your example would be that you could deploy exactly the same code with a different configuration with 1 installation using LDAP and the other using MySQL authentication.
Refactor type hinting ("AbstractLoginAdapter") to ("MySQLLoginAdapter").
If you call abstract class method in the new __CLASS__ // Fatal Error.

Categories