I want to mock a service that is required in a class constructor. I have an exception of PHPUnit : MyService is required, Mock_MyService_0afc7fc1 given.
But with the Request, EntityManager or other Symfony 2 component, I haven't this issue.
Here is my Class's construct :
use Acme\Bundle\Service\MyService;
use Symfony\Component\HttpFoundation\Request;
...
public function __construct(MyService $service, Request $request)
{
and my mock :
...
$service = $this->getMock('MyService');
$class = new Class($service, $request);
It's impossible to mock our own service ? Only Symfony 2 component ?
PS : If I delete MyServicelike that : public function __construct($service, Request $request), this works. But I want to define my variable with it :(
The issue is that PHPUnit at the time of the test execution can't find (or autoload) your MyService class.
That means that you'll probably run into the same issues with other Mocking libraries as all of them require the original class to exist to scan it and create the mock.
It happens because you need to tell PHPUnit the Fully-Qualified Class Name.
Change your code to $this->getMock("\Acme\Bundle\Service\MyService"); and it should work out.
(Still, give mockery a try. It's a nice library)
Related
I have a couple of days something lost with the declaration of services, especially when passing parameters to custom classes.
I have several extensions and listeners declared as services, to which I pass parameters to the constructor and operating properly but do the same for a custom class and does not work.
The error is that the class does not receive parameters in the constructor.
Error:
Catchable Fatal Error: Argument 1 passed to Consolidador\PanelBundle\Controller\Integration::__construct() must be an instance of Doctrine\Bundle\DoctrineBundle\Registry, none given, called in /var/www/vhosts/consolidadordeviajes.com/httpdocs/src/Consolidador/PanelBundle/Controller/HotelsController.php on line 149 and defined
This is the service that stated in app/config/services.yml:
integration:
class: Consolidador\PanelBundle\Controller\Integration
arguments: ['#doctrine', '#session']
This is the custom class:
<?php
namespace Consolidador\PanelBundle\Controller;
use Doctrine\Bundle\DoctrineBundle\Registry as Doctrine;
use Symfony\Component\HttpFoundation\Session\Session;
class Integration
{
private $em;
private $session;
public function __construct(Doctrine $doctrine, Session $session)
{
$this->em = $doctrine->getEntityManager();
$this->session = $session;
}
...
When I call the custom class is when the error occurs, I think I'm stating the service and collecting the parameters correctly, if not, I hope that other eyes may see the error that I get not find.
A greeting and thanks.
SOLUTION
The problem was simply that instead of calling the custom class from e lcontendor dependency, trying instalanciarla manually. Now I know that the services have to call them:
$foo= $this->get('service_name');
$foo-Method();
Is it possible to get the doctrine service inside a bundle extension?
I can access the container, but can't get the doctrine service.
...
class UltroExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container){
$dm = $container->get('doctrine_mongodb');
...
}
}
I get this error: The service definition "doctrine_mongodb" does not exist.
The container hasn't been built/compiled yet - that's why you only get a ContainerBuiler object passed to the load() method.
you can't get a service object from the builder as it's not holding the services but only the service definitions at that point.
Use a compiler pass instead. More information can be found in the documentation chapter Compiling the container.
Maybe your problem can be solved using a service factory, too.
I make some functional test with Symfony 2 and phpunit.
But i've some trouble with a Service.
Let me explain.
During my run test, i want to use some service used by the application. So i juste set my setUp function for setting the kernel :
static::$kernel = static::createKernel();
static::$kernel->boot();
$this->objectFactory = static::$kernel->getContainer()->get('some.application.objectfactory');
So i've this and in my function i need to used a service that return an object so i call my service like that
$var = $this->objectFactory->getObject($id);
and obviously in my tearDown function i just :
protected function tearDown()
{
$this->client->restart();
unset($this->client, $this->objectFactory);
}
So my problem is when i run a test i've this message :
Symfony\Component\DependencyInjection\Exception\InactiveScopeException: You cannot create a service ("request") of an inactive scope ("request").
And i can't find a way to solve this.
Did someone have any idea ??
My version of Symfony is 2.2.1 and my version of phpunit is 3.7.19
If someone can help me, i could be very happy.
I'm sorry if my English isn't so good.
EDIT
Maybe it could help someone, in the service i used that : :
$request = $this->container->get('request');
It seems to be the reason why it dosen't work, when i remove it, it doesn't say the error, but they still doesn't work.
EDIT
#Cyprian
According to you have change my code for what i want.
So i just add to my service, in the function that i want, the client (Client web test case), and then inside the function i just add this :
if (isset($client)) {
$request = $client->getRequest();
} else {
$request = $this->container->get('request');
}
So in my function where i call the service i've just this :
public function getObject($id)
{
//Get the service from the kernel
$service = static::$kernel->getContainer()->get('service');
$object = $service->getObject($id, $this->client);
}
and it works fine like this
#nifr
Your idea doesn't work for me, but i think your idea wasn't wrong, they just not works in my case
However Thanks for your help, i'm happy i works now, and i expect that post could help someone else
Try get request from the client, not service container:
$request = $this->client->getRequest();
In that way you can also get kernel and/or container:
$kernel = $this->client->getKernel();
$container = $this->client->getContainer();
One more useful tip: kernel from the client is rebooted between each two requests. So, for example if you pass your mock to client's container and do some request, in next request (after the first one) the container will not contain your mock.
There is no request available in phpUnit as long as you don't construct one.
If you want to test a request. create it like this:
use Symfony\Component\HttpFoundation\Request;
protected $request;
public function setUp()
{
// ...
$this->request = new Request();
// ... modify your request acccording to your needs
}
and add/call a setter in your Service using the request.
$service = $this->kernel->getContainer()->get('your_service')
$service->setRequest($this->request);
or create a Functional Test with WebtestCase.
When I try to inject the #request into any of my services, I get this exception:
ScopeWideningInjectionException: Scope Widening Injection detected:
The definition "service.navigation" references the service "request"
which belongs to a narrower scope. Generally, it is safer to either
move "service.navigation" to scope "request" or alternatively rely on
the provider pattern by injecting the container itself, and requesting
the service "request" each time it is needed. In rare, special cases
however that might not be necessary, then you can set the reference to
strict=false to get rid of this error.
What is the best way to proceed? Should I try to set this strict=false and how, or should I NOT inject the request service, but rather pass it to the service through my controller each time I call functions I need?
Other possibility would be to inject the kernel and take it from there, but in my service I am using only #router and #request, so injecting the whole kernel would be irrational.
In Symfony 2.4, this has changed. Now, you can inject the 'request_stack' service.
For example:
use Symfony\Component\HttpFoundation\RequestStack;
class MyService
{
protected $request;
public function setRequest(RequestStack $request_stack)
{
$this->request = $request_stack->getCurrentRequest();
}
}
In your config.yml:
services:
my.service:
class: Acme\DemoBundle\MyService
calls:
- [setRequest, ["#request_stack"]]
Full documentation is here: http://symfony.com/blog/new-in-symfony-2-4-the-request-stack
I think there may have been some misunderstanding about what the official documentation says. In most cases you do want to inject the request directly with a scope="request" attribute on the service element. This makes the Scope Widening go away.
<service
id="zayso_core.openid.rpx"
class="Zayso\CoreBundle\Component\OpenidRpx" public="true" scope="request">
or in yml
zayso_core.openid.rpx:
class: Zayso\CoreBundle\Component\OpenidRpx
public: true
scope: request
It's only in specific special cases such as Twig extensions where you need to inject the container.
And kernel is not even mentioned in the page on scopes. Injecting the kernel is far worse (conceptually) than injecting a container.
UPDATE: For S2.4 and newer, use #Blowski's answer below.
NB: This answer was written back in 2012, when Symfony 2.0 was out and then it was the good way to do!
According to the official documentation it is usually not required to inject request into your services. In your service class you can pass kernel container (injecting it is not a big overhead, as it sounds), and then access request like this:
public function __construct(\AppKernel $kernel)
{
$this->kernel = $kernel;
}
public function getRequest()
{
if ($this->kernel->getContainer()->has('request')) {
$request = $this->kernel->getContainer()->get('request');
} else {
$request = Request::createFromGlobals();
}
return $request;
}
This code is also working fine when service is accessed in CLI (eg, during unit-testing).
The best way i found to make a service use the request service, not rely on the whole container and still not be required to have the request scope, was to make a RequestInjector service which takes the container. then you inject that into the service that wants to use the request object
class RequestInjector{
protected $container;
public function __construct(Container $container){
$this->container = $container;
}
public function getRequest(){
return $this->container->get('request');
}
}
class SomeService{
protected $requestInjector;
public function __construct(RequestInjector $requestInjector){
$this->requestInjector = $requestInjector;
}
}
for services.yml
request_injector:
class: RequestInjector
public: false
arguments: ['#service_container']
some_service:
class: SomeService
arguments: ['#request_injector']
The way I've found, and I'm sure it's probably not the best way (May not even be recommended), is to define the request service as synthetic.
Edit: Indeed, this is not recommended, because it disables the scope sanity checks.
This thread contains a good explanation of why Symfony is throwing that exception:
http://groups.google.com/group/symfony-devs/browse_thread/thread/a7207406c82ef07a/e2626c00f5cb9749
In your services.xml:
<service id="request" synthetic="true" />
<service id="my_service" class="......">
<argument type="service" id="request" />
</service>
Per the docs, it's better if you place your service in the request scope, or just inject the service container.
If you can't use RequestStack directly, you could create a factory service that returns the current request using RequestStack.
# services.yml
app.request:
class: Symfony\Component\HttpFoundation\RequestStack
factory: [ #request_stack, getCurrentRequest ]
Then you can access the current request using the app.request service.
another way to inject currentRequest directly:
setter injection:
calls:
- ['setRequest', ['#=service("request_stack").getCurrentRequest()']]
or constrauctor injection:
arguments:
$request: '#=service("request_stack").getCurrentRequest()'
I think it's more important to focus on getting the request instead of setting it. I would do something similar to #Blowski's solution, except using a getter. This is very similar to the documentation's example.
namespace Acme\HelloBundle\Newsletter;
use Symfony\Component\HttpFoundation\RequestStack;
class NewsletterManager
{
protected $requestStack;
public function __construct(RequestStack $requestStack)
{
$this->requestStack = $requestStack;
}
protected function getRequest()
{
return $this->requestStack->getCurrentRequest();
}
public function foo()
{
$request = $this->getRequest();
// Do something with the request
}
}
And your services.yml config file.
services:
newsletter_manager:
class: Acme\HelloBundle\Newsletter\NewsletterManager
arguments: ["#request_stack"]
Now you're always sure that you're getting the correct request, and you don't have to worry about setting/re-setting the request.
As #simshaun states its best practice to place your service in the request scope. This makes the purpose of the service quite clear.
Note that this will make your service unavailable in other scopes such as the command line. However if your service relies upon the request, you should not be using it on the command line anyway (because there is no request available on the command line.
There was 2 questions here saying injecting the whole service container should solve this. But question ... see below (note difference between try 2 & 3) ...
Try 1
public function __construct(SecurityContext $securityContext) {
$this->securityContext = $securityContext);
}
Curcular Reference. Okay ...
Try 2
public function __construct(ContainerInterface $container) {
$this->securityContext = $container->get('security.context');
}
Circular Reference (Why?, I am injecting the container like in try 3 except I got the security context only)
Try 3
public function __construct(ContainerInterface $container) {
$this->container = $container;
}
Works.
This happens because your security context depends on this listener, probably via the entity manager being injected into a user provider. The best solution is to inject the container into the listener and access the security context lazily.
I typically don't like injecting the entire container into a service, but make an exception with Doctrine listeners because they are eagerly loaded and should therefore be as lazy as possible.
As of Symfony 2.6 this issue should be fixed. A pull request has just been accepted into the master. Your problem is described in here.
https://github.com/symfony/symfony/pull/11690
As of Symfony 2.6, you can inject the security.token_storage into your listener. This service will contain the token as used by the SecurityContext in <=2.5. In 3.0 this service will replace the SecurityContext::getToken() altogether. You can see a basic change list here: http://symfony.com/blog/new-in-symfony-2-6-security-component-improvements#deprecated-the-security-context-service
Example usage in 2.6:
Your configuration:
services:
my.listener:
class: EntityListener
arguments:
- "#security.token_storage"
tags:
- { name: doctrine.event_listener, event: prePersist }
Your Listener
use Doctrine\ORM\Event\LifecycleEventArgs;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
class EntityListener
{
private $token_storage;
public function __construct(TokenStorageInterface $token_storage)
{
$this->token_storage = $token_storage;
}
public function prePersist(LifeCycleEventArgs $args)
{
$entity = $args->getEntity();
$entity->setCreatedBy($this->token_storage->getToken()->getUsername());
}
}
The reason "2" fails and "3" does not is because in option 2 you are trying to access the security context immediately from the container when it is likely not populated yet.
As best I can tell, Symfony2 parses through the config and instantiates the service one after the other and then moves onto the handling the rest of the request.
This means you cannot necessarily access the various parts of the container because it may be loading them in a different order. So you have the memory pointer to the container, and store that, but then let the framework finish building the full container before you try to access parts of it. A notable exception to this is when you directly inject the service into another service, at which point the container is making sure it has that service loaded first.
You can see the effects of this by making two services. A and B. A is passed B, and B is passed A. Now you have a circular reference. If you instead passed the container into both A and B, you could not access A from B and B from A without a problem.
You should always try to avoid injecting container directly to your services.
I think the best possible solution to the «circular reference» problem as well as to possible performance issues, would be to use «Lazy Services» feature available starting from Symfony 2.3.
Just mark you dependency as lazy in your service container configuration and install ProxyManager Bridge (look for details in Lazy Services documentation above).
I hope that helps, cheers.