Functional Test - Mock service does not persist in service container - php

I am hoping someone can shed some light on this issue I am facing.
[PROBLEM]
I have mocked out doctrine.orm.default_entity_manager service in my functional unit test. I inject this into the client service container so that I do not have to hit my DB during the course of my functional test. For my test that just involve a GET request I am able to verify that the controller I am testing is using my mocked service.
However, if I attempt to do a POST request by using the crawler with a form submission my mocked service does not persist. After the initial GET request the client seems to just inject doctrine.orm.default_entity_manager service again as it needs it and not my mocked version that I set in the clients service container.
In summary, during the GET request my mocked service is being used, but during the POST request EntityManager5144076565ee8_546a8d27f194334ee012bfe64f629947b07e4919__CG__\Doctrine\ORM\EntityManager is being used.
[SEE CODE SNIPPET BELOW]
[QUESTION]
Is it possible to do what I am asking? I would like to have ALL my requests use the mocked service I defined. I want to have a functional test but avoid writing or reading from the database.
[SAMPLE CODE]
// Mocks
$entityRepository = $this
->getMockBuilder('Doctrine\ORM\EntityRepository')
->setMethods(array('findby'))->disableOriginalConstructor()
->getMock();
$entityRepository->expects($this->any())->method('findBy')
->will($this->returnValue(array()));
$em = $this->getMockBuilder('Doctrine\ORM\EntityManager')
->setMethods(
array('getRepository', 'getClassMetadata', 'flush',
'persist'))->disableOriginalConstructor()
->getMock();
$em->expects($this->any())->method('flush')
->will($this->returnValue(FALSE));
$em->expects($this->any())->method('persist')
->will($this->returnValue(FALSE));
$em->expects($this->any())->method('getRepository')
->will($this->returnValue($entityRepository));
$em->expects($this->any())->method('getClassMetadata')
->will($this->returnValue(new ClassMetadata("test")));
// Create test client.
$client = static::createClient();
// Inject entity mock into service container.
$client->getContainer()
->set('doctrine.orm.default_entity_manager', $em, 'container');
// Define request
$crawler = $client->request('GET', '/locations/types/add');
// Verify a few things
$form = $crawler->selectButton('submit')->form();
$form['location_type[title]'] = "TEST TITLE";
$form['location_type[description]'] = "TEST DESCP";
$crawler = $client->submit($form);

The issue here is that the kernel is booted after (during) every request:
protected function doRequest($request)
{
// avoid shutting down the Kernel if no request has been performed yet
// WebTestCase::createClient() boots the Kernel but do not handle a request
if ($this->hasPerformedRequest) {
$this->kernel->shutdown();
} else {
$this->hasPerformedRequest = true;
}
if ($this->profiler) {
$this->profiler = false;
$this->kernel->boot();
$this->kernel->getContainer()->get('profiler')->enable();
}
https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/FrameworkBundle/Client.php
So you need to replace doctrine with your mock after each request:
// Inject entity mock into service container.
$client->getContainer()
->set('doctrine.orm.default_entity_manager', $em, 'container');
// Define request
$crawler = $client->request('GET', '/locations/types/add');
// Inject entity mock into service container.
$client->getContainer()
->set('doctrine.orm.default_entity_manager', $em, 'container');
An easier way to use your mock globally would be to override the doctrine settings in config_test.yml
orm:
default_entity_manager: Acme/MyBundle/Test/MockDoctrineEM
http://symfony.com/doc/master/reference/configuration/doctrine.html

Related

Testing Laravel controller internals with a mock

So I have this Laravel controller and I want to test it.
It is an OAuth client, so a callback is needed to finish the setup.
I want to test the OAuth callback.
The code is something like this:
public function callback(): \Illuminate\Http\RedirectResponse
{
$aCookie = request()->cookie('cookie');
$authCode = request()->get('code')
$connection = OAuthpackage::getConnection();
// Store the credentials in the database
}
To test if the credentials are stored correctly in de database I want to mock the OAuthpackage because it is fine to give it face credentials.
My first thought was to just test the controller by calling that directly. The thing with that is that I don't only need to mock the Oauthpackage but also the request class because I need to face the cookie getting in the code in the GET request.
Now I read on the internet that you probably would not want to mock the request class.
So I thought about just doing the request in the test and then seeing the output.
1 It is just the regular flow
2 I need only one mock
This is what I came up with:
public function testAppCallback()
{
$user = Auth::user();
$connectionFactory = $this->createMock(OAuthPackage::class);
$connectionFactory->method('getConnection')->willReturn(new DummyConenection());
$this->disableCookieEncryption();
$response = $this->withCookies([
'cookie' => 'http://localhost',
])->get('/oauth?code=my_auth_code');
}
The DummyClass just inherits from the real class, but this way I can test its type in the debugger. Turns out that the DummyClass is not being used.
It seems like Laravel boots up a whole new instance as soon as I make a web request and therefore forgets all about the DummyClass.
How should I go about to solve this problem?

Functional tests in symfony3, access to container after submitting a form

I'm writing functional tests for my Symfony3 application. I have a test which looks like this:
public function testList()
{
$client = static::createClient();
$client->getCookieJar()->set($this->cookie);
$this->sender->method('isSuccessfull')->will($this->returnValue(true));
$container = $client->getContainer();
$container->set('app.service1', $this->object1);
$container->set('app.service2', $this->object2);
$crawler = $client->request('GET', '/list/1');
$form = $crawler->selectButton('Save')->form();
$client->submit($form);
}
Everything is good until submitting form. Kernel losing the setted container services while submitting a form. How can I these services into container also after submitting a form? Maybe there is other option to resolve my problem?
If you check the source code for Symfony\Component\HttpKernel\Client::doRequest() class you can see that it terminates the kernel which is then started again later and that's why you loose all service you created manually.
I guess you have an app which you're testing so you could add the services to its services.yml. Another way could be extending the Client class with your own and overriding getContainer() method to always add these extra services (then you'd have to update the service definition for test.client in a compile pass with your customized class).

Zend setting the authorization header for unit testing using PHPUnit

Recently I tried to test my REST API's using PHPUnit.
I am facing problem to send http authorization header for my test case.
Every time I do that I get an 403 response instead of 200
Here is my code :
<?php
use Zend\Test\PHPUnit\Controller\AbstractHttpControllerTestCase;
use Zend\Http\Request;
use Zend\Http\Headers;
use Zend\Http\Response;
class TrialTest extends AbstractHttpControllerTestCase
{
protected $traceError = true;
public function setUp()
{
$this->setApplicationConfig(
include 'config/application.config.php'
);
parent::setUp();
}
public function testAction()
{
$this->request = new Request();
$this->getRequest()->setMethod('GET');
//$headers = new \Zend\Http\Headers;
//$header = $headers->addHeader($headers->fromString('Authorization:Bearer test'));
$this->getRequest()->sendHeaders('Authorization:Bearer test');
//var_dump($headers);
//$this->getRequest()->setHeaders($header);
$this->dispatch('/campaign');
$this->assertResponseStatusCode(200);
}
}
Kindly help !! where am I going wrong ?
Try setting your headers like this:
$headers = new \Zend\Http\Headers;
$headers->addHeaderLine('Authorization', 'Bearer test');
$this->request->setHeaders($headers);
And you have to make sure that test a valid OAuth token otherwise it will never work. I am not so sure if a 4 character token will ever validate correctly...
UPDATE
I think there is a general problem with your test design. You only set the request object in the controller instance, but the service taking care of authentication has no access to this request object and thus it will not authorize the request correctly.
If you write a controller test in which you test the route '/campaign' you should only test the controller functionality and set mocks for all dependencies. I think the main problem starts in your setUp method. To test this controller you should not load your whole application.config.php. You should set an MvcEvent instance and attach all you need to this event (the correct Router instance, etc) and then dispatch the controller.
Check a proper example of such a ZF2 controller test here.
Testing your OAuth module should happen in an independent test.

Mock a request in phpunit

I am trying to write a unit test (phpunit) to cover a controller action. I am receiving problems regarding invalid scope for the getRequest() call.
note: I am a newbie to Symfony2 and TDD (phpunit)
I guess this is because there is no request as such?
My questions are:
Can I mock a request object?
Am I approaching this in the right way? Should I be placing the bulk of the code into a service and then unit testing the service and only FUNCTIONAL test the controllers?
I think knowing the principle going forward is what I'm after, rather than the lines of code.
There is a mock request built into the web test case. Just extend that and use the crawler to make the request.
For example:
public function testMyController()
{
$client = static::createClient();
$router = $this->container->get('router');
$url = $router->generate('routeName');
$crawler = $client->request('GET', $url);
// check we get a 200
$this->assertEquals(200, $client->getResponse()->getStatusCode(), "Unexpected HTTP status code for API Config call");
}

Zend Framework 2 Restful web service: common functionality across controllers

Im making a restful web service with Zend FW 2. How can I create a system, that checks ie. API key everytime REST is called? Checking the key in every controller in every function of course is not the way to go, so Im looking for something "global".
Thanks!
Assuming all your rest methods are in one controller you can listen to that controllers dispatch event, using a high priority so checks are done early...
Register the listener in your modules bootstrap, for example assuming you added an ApiController to the Application module
public function onBootstrap(EventInterface $e)
{
$app = $e->getApplication();
// get the shared events manager
$sem = $app->getEventManager()->getSharedManager();
// listen to dispatch event when triggered by the ApiController
$sem->attach('Application\Controller\ApiController', 'dispatch', function($e) {
// do your api key checks
// if checks fail get the response from the controller
$controller = $e->getTarget();
$response = $controller->getResponse();
$response->setStatusCode(401);
// return $response, short circuiting dispatch event
return $response;
}, 9000); // 9000 = high priority, do this early
}
Point of note, the event passed to your closure contains as its target an instance of your controller, so if you need to get services from the ServiceManager to do your api checks you can do so just like you would in the controller itself, ie...
$controller = $e->getTarget();
$sm = $controller->getServiceLocator();

Categories