I am new to Symfony, I have wrote small app now have to add unit tests, here is my controller:
<?php
namespace myBundle\Controller;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\RedirectResponse;
class IndexController extends AbstractController
{
/**
* #param \Symfony\Component\HttpFoundation\Request $request
* #return \Symfony\Component\HttpFoundation\Response
*/
public function indexAction(Request $request)
{
if ($this->getRequest()->isMethod('POST')) {
// Do Something
}
// Do Something otherwise
}
}
My test:
class IndexControllerTest extends \PHPUnit_Framework_TestCase
{
protected $testController;
public function setUp()
{
$this->testController =
$this->getMockBuilder('myBundle\Controller\IndexController')
->disableOriginalConstructor()
->getMock();
}
public function testPostSaveActionWithBadRequest()
{
$expectation = 'Some text ';
$response = $this->testController->indexAction(new Request);
$this->assertInstanceOf(
'Symfony\Component\HttpFoundation\JsonResponse',
$response
);
$content = json_decode($response->getContent());
$this->assertEquals($expectation, $content->error);
}
}
When I run this test I get following:
PHP Fatal error: Call to a member function get()
which is basically on following line
if ($this->getRequest()->isMethod('POST')) {
this tells me the container is null (I verified it by printing dump of the container).
any idea what am I missing here or is there a way to provide container as dependency for that test.
I really appreciate all the help.
thanks
FI
You're trying to mock the class you're suppose to test:
$this->testController =
$this->getMockBuilder('myBundle\Controller\IndexController')
->disableOriginalConstructor()
->getMock();
You should actually instantiate the class you're testing, and mock or stub its collaborators.
However, in this particular scenario, instead of writing a unit test, write a functional test. There's a chapter on writing functional tests in Symfony docs that'll help you.
Your controller uses lots of framework classes (classes that don't belong to you), and you shouldn't mock them either. That's why functional tests are better in this case. Also, make sure you move as much code as possible out of your controller, so you can properly unit test that part (and write as little functional tests as possible).
In the meantime read some books on unit testing (in the following order):
TDD by Example
Growing Object-Oriented Software Guided by Tests
Related
I'm still in the process of learning about Laravel and Dependency Injection. I understand the concept, but I don't know how to mock a dependency in this specific case:
MyController.php
use Illuminate\Routing\Controller;
use MyPackage\Services\ServiceInterface;
class MyController extends Controller{
protected $service;
public function __construct(ServiceInterface $service)
{
$this->service = $service;
}
}
MyServiceProvider.php
use Illuminate\Support\ServiceProvider;
class MyServiceProvider extends ServiceProvider{
public function register()
{
$this->app->bind('MyPackage\Services\ServiceInterface', function ($app) {
return new MyPackage\Services\ConcreteService(['key'=>'123'], $app->make('GuzzleHttp\Client'));
});
}
}
So, as you can see, I have a controller that requires an instance of ServiceInterface. That instance is being resolved in the ServiceProvider. The constructor of ConcreteService requires a client to perform Http request to an API. This Http is being resolved by the Service container (It will be an instance of Guzzle).
Now, how can I mock this instance of Guzzle on my tests?
The ideal result is doing something like this:
MyTest.php
...
$this->post(route('routeToActionInMyController'), $params);
So, in my tests I just need to hit the route that will be using an specific method of MyController.php but I don't need a "real" Guzzle instance. I just need to mock the response to test if MyController behaves in the expected way (and stores things in the database properly).
How can I instruct the Service Container to inject a Mocked object during tests only? Or am I doing this in the completely wrong way?
Any help will be appreciated.
Thanks in advance
In your test class:
class TestingSomething extends TestCase {
protected function setUp() {
parent::setUp();
$mockServiceInterface = $this->getMockBuilder(ServiceInterface::class)->getMock();
$this->app->instance(ServiceInterface::class,$mockServiceInterface);
}
public function testPostToRoute() {
$this->post(route('routeToActionInMyController'), $params);
}
}
This should replace what's already bound in the service container with that mock instance.
Refer to the PHPUnit manual on chapter 9. Test doubles for what you can do with the mock builder and resulting mocks.
I like to ask you about your practice of writing unit test, how to mock object and don't repeat the code?
I am using Symfony2 framework and for example I have many bundles with custom validators. When me and the rest of my team writing unit test we repeat the code of mocking Constraint, ExecutionContext, ConstraintViolationBuilderInterface. I know that we can create trait or abstract class or something else where we can store code responsible for mocking but before I will start doing this I like to know your best practices.
My first idea was to create a class/trait which will store for example mock of all repositories. Example:
class MockRepositoryHelper extends \PHPUnit_Framework_TestCase
{
public function getUserRepositoryMock()
{
return $this->prophesize(UserRepository::class);
}
// next repositories getters
}
and then use this code in real test case:
class EmailValidator extends \PHPUnit_Framework_TestCase
{
private $mockRepositoryHelper;
public function setUp()
{
parent::setUp();
$this->mockRepositoryHelper = new MockRepositoryHelper();
}
/**
* #test
*/
public function it_should_find_user()
{
$userRepository = $this->mockRepositoryHelper->getUserReposioryMock();
$userRepository->findUser(Argument::type('string'))->willReturn(null);
// rest of the test
}
}
Of course this is only pseudo code what I imagined, my first thought. What are yours ideas?
Basically I am asking how to write unit test faster and don't repeat the code?
I posted another question trying to find a way to statically access a repository class outside of a controller in a custom "helper" class.
So far the only way I have figured out how to achieve this is using the code below. If anyone wants to chime into the other question about "best practice" or "design patterns" please do.
I opened this question to seek the best method on having a singleton service (?) loaded when symfony boots so other classes can access it statically without any dependency injection. I haven't had much luck on finding any official docs or common practices. I know singleton is anti practice, but is the method below the best way, or is there a more ideal solution?
services.yml
parameters:
entity.device: Asterisk\DbBundle\Entity\Device
services:
asterisk.repository.device:
class: Asterisk\DbBundle\Entity\Repositories\DeviceRepository
factory: ["#doctrine.orm.asterisk_entity_manager", getRepository]
arguments:
- %entity.device%
tags:
- {name: kernel.event_listener, event: kernel.request, method: onKernelRequest}
DeviceRepository
class DeviceRepository extends \Doctrine\ORM\EntityRepository
{
/** #var ExtendedEntityRepository */
protected static $instance;
public function __construct(EntityManager $entityManager, ClassMetadata $class)
{
parent::__construct($entityManager, $class);
if(static::$instance instanceof static == false)
static::$instance = $this;
}
public static function getInstance()
{
return static::$instance;
}
public function onKernelRequest($event)
{
return;
}
}
Glad to see you are not running around anymore.
Your approach is not going to work unless someone grabs the repository out of the container first so self::$instance is initialized. But you really don't want to do this anyways. Super hacky.
You want to inject the repository service into your kernel listener. Trying to make the repository act as a kernel listener is just not a good design. So just make a service for your repository and then a second one for the listener. It may seem a bit strange at first but it really does work well in practice and it's the way S2 is designed.
If for some reason you are stuck with the notion that you have to be able to access the container globally then be aware that your kernel is defined globally(take a look at app.php) and it has a getContainer method in it.
$repo = $_GLOBAL['kernel']->getContainer()->get('asterisk.repository.device');
But again, there should be no need to do this.
==============================
Update - It looks like you are trying to use the listener functionality just to setup singletons. You should try to avoid singletons but if you really think you need them then the global access to the kernel can be used:
class DeviceRepository extends \Doctrine\ORM\EntityRepository
{
/** #var ExtendedEntityRepository */
protected static $instance;
public static function getInstance()
{
if (!static::$instance) {
static::$instance = $_GLOBAL['kernel']->getContainer()->get('asterisk.repository.device');
}
return static::$instance;
}
Poor design but at least it get's rid of the listener hack and it avoids creating the repository until it's actually needed. It aslo means you can access the repository from commands (listeners are not setup when commands are called).
I do not understand what the profit will be about this method. The idea of the servicecontainer is to make just one instance of each class and give a reference (or pointer if you like) to any method who asks to use this same instance. Let me proof it:
Service definition:
// app/config.yml
services:
app.test:
class: Vendor\AppBundle\Service\Test
and a custom class:
// src/AppBundle/Service/Test.php
namespace AppBundle/Service;
class Test {
public $test = 0;
}
and a controller:
// src/AppBundle/Controller/DefaultController
namespace AppBundle/Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
class DefaultController extends Controller
{
/**
* #Route("/", name="homepage")
*/
public function indexAction()
{
$instance1 = $this->get('app.test');
$instance2 = $this->get('app.test');
$instance1->test = 1;
echo $instance2->test; // RETURNS 1 !!!
exit;
}
I know it's possible to test private/protected methods with PHPUnit using reflection or other workarounds.
But most sources tell me that it's not best practice to write tests for private methods inside of a class.
You are supposed to test the class as if it were a "black box" — you just test for expected behavior by comparing inputs with outputs disregarding the internal mechanics. Writing tests for classes should also notify you to unused private methods, by showing lack of code coverage.
When I test my class and generate an HTML report, it shows the private methods as not covered by tests, even though the lines from which they are called are absolutely executed/covered. I know that the private methods are executed, because if they weren't the assertions on my class would not pass.
Is this expected behavior in PHPUnit? Can I strive for 100% coverage, while still testing private methods only indirectly?
Some simplified example code (using RestBundle in Symfony2):
class ApiController extends FOSRestController {
/*
* #REST\View()
* #REST\Get("/api/{codes}")
*/
public function getCodesAction($codes) {
$view = new View();
$view->setHeader('Access-Control-Allow-Origin', '*');
$view->setData(array('type' => 'codes','data' => $this->_stringToArray($codes)));
$view->setFormat('json')->setHeader('Content-Type', 'application/json');
return $this->handleView($view);
}
private function _stringToArray($string){
return explode('+',$string);
}
The public function shows as "covered", the private function is indirectly covered but shows colored red in PHPUnit reports.
Test:
class ApiControllerTest extends WebTestCase {
public function test_getCodesAction(){
$client = static::createClient();
$client->request('GET', '/api/1+2+3');
$this->assertContains('{"type": "codes", "data": [1,2,3]}', $client->getResponse()->getContent());
}
}
This is just a silly example of course, I could just as well include the explode() right there in the public function; But the controllers I'm writing tests for contain much more intricate and re-usable private functions which transform data in more complex ways (but are still side-effect free).
In Phpunit you can specify the Covered Methods with special annotation, as descrived in the doc.
You can do something like this:
class ApiControllerTest extends WebTestCase {
/**
* #covers ApiController::getCodesAction
* #covers ApiController::_stringToArray
*/
public function test_getCodesAction(){
$client = static::createClient();
$client->request('GET', '/api/1+2+3');
$this->assertContains('{"type": "codes", "data": [1,2,3]}', $client->getResponse()->getContent());
}
}
Hope this help
I have to write a test class to test my service methods. In my controllers I could access the service by doing $service = $this->get('myService'); and I could access my methods by doing $service->someMethod();.
Now I want to write a test class to test some of the service methods, I tried doing like the doc :
class ServiceTest extends \PHPUnit_Framework_TestCase {
public function testSomeMethod() {
$service = $this->get('myService');
....
}
}
When I launch the test I get something like : Fatal error : Call to undefined method XXX\XXXBundle\Tests\Services\ServicesTest::get() in ...
So my question is how can I use get method to be able to call my service methods
You don't need to use the container in order to test services and other classes.
You should create a new instance of the class and inject the dependencies using test doubles.
Test doubles
- since you don't want to write a functional testing and to use the real dependencies you should be using a test double.
for example if one of the service dependencies is the EntityManager what would happen is that a new entries would be inserted into the Database (or deleted from it) and its not the purpose of Unit testing (you do need to check this tho if you are writing a functional test).
And that's how you should approach this kind of testings:
public function testSomeServiceAdd()
{
$dependencyOne = $this->getMockBuilder('Acme\SomeBundle\DependencyOne')->
disableOriginalConstructor()->
getMock();
$dependencyTwo = $this->getMockBuilder('Acme\SomeBundle\DependencyTwo')->
disableOriginalConstructor()->
getMock();
$service = new SomeService($dependencyOne, $dependencyTwo);
$response = $service->add(1, 2);
$this->assertEquals($response, 3);
}
As you can see I am "mocking" the dependencies and injecting it into the service, after that i'm calling the method and asserting the result.
I would also like to suggest a Mocking framework instead of using the built-in PHPUnit mocking functions. it's easier to use and it has much more mocking functionality:
https://github.com/danrevah/ShortifyPunit
To test your services you should extend the KernelTestCase class.
class ServiceTest extends KernelTestCase
{
private $service;
/**
* {#inheritDoc}
*/
public function setUp()
{
self::bootKernel();
$this->service = static::$kernel->getContainer()
->get('my_service')
;
}
}