How to unit test Symfony controllers - php

I'm trying to get a Symfony controller in a test harness using Codeception. Every method starts as follows:
public function saveAction(Request $request, $id)
{
// Entity management
/** #var EntityManager $em */
$em = $this->getDoctrine()->getManager();
/* Actual code here
...
*/
}
public function submitAction(Request $request, $id)
{
// Entity management
/** #var EntityManager $em */
$em = $this->getDoctrine()->getManager();
/* 200+ lines of procedural code here
...
*/
}
I've tried:
$request = \Symfony\Component\HttpFoundation\Request::create(
$uri, $method, $parameters, $cookies, $files, $server, $content);
$my_controller = new MyController();
$my_controller->submitAction($request, $id);
from my unit tests, but it seems there's a lot of other setup I don't know about that Symfony does in the background. Every time I find a missing object and initialise it, there's another one that fails at some point.
I've also tried stepping through the test from PhpStorm, but PhpUnit has some output that causes Symfony to die before it gets anywhere near the code I'm trying to test because it can't start the $_SESSION after any output has occurred. I don't think this happens from the command line, but I'm not really close enough to tell yet.
How can I simply and extensibly run this code in a Unit Test?
A bit of background:
I inherited this code. I know it's dirty and smells because it's doing model logic in the controller. I know that what I'm asking for is not a "pure" unit test because it touches virtually the whole application.
But I need to be able to run just this "small" (200+ lines) bit of code automatically. The code should run in no more than a couple of seconds. I don't know how long because I've never been able to run it independently.
Currently, the setup time to run this code through the website is huge, as well as being complex. The code does not generate a web page, it's basically an API call that generates a file. I need to be able to generate as many of these test files as I like in a short amount of time as I'm making coding changes.
The code is what it is. It's my job to be able to make changes to it and at the moment I can't even run it without a huge overhead each time. It would be irresponsible to make changes to it without knowing what it's doing.

Part of your problem is, that when you write a Controller that extends from Symfony's base Controller or the new AbstractController it will load other dependencies from the container. These service you either have to instantiate in your tests and pass them to a container, that you then set in your controller like this:
$loader = new Twig_Loader_Filesystem('/path/to/project/app/Resources/views');
$twig = new Twig_Environment($loader, array(
'cache' => '/path/to/app/var/cache/templates',
));
# ... other services like routing, doctrine and token_storage
$container = new Container();
$container->set('twig', $twig);
$controller = new MyController();
$controller->setContainer($container);
or mock them, which makes your test pretty much unreadable and break on every change you do to the code.
As you can see, this is not really a unit test, because you will need all the services you pull from your container directly by calling $this->get()/$this->container->get() or indirectly, such via the helper methods in the controller, e.g. getDoctrine().
Not only is this tedious if you don't configure the services in the same way as you use in production, your tests might not be very meaningful, as they could pass in your tests but fail in production.
The other part of your problem is the comment inside your snippet:
200+ lines of procedural code here
Without seeing the code I can tell you that properly unit testing this is near impossible and not worth it.
The short answer is, you can't.
What I recommend is either writing Functional Tests using WebTestCase or something like Selenium with CodeCeption and test your controller indirectly through the UI.
Once you have tests covering the (main) functionality of your action, you can start refactoring your controller splitting things into smaller chunks and services that are easier to test. For those new classes unit tests make sense. You will know when the website works again as before your changes, when your Functional Tests are green (again). Ideally you would not need to change these first tests as they only look at your website through the browser and therefore are not affected by any code changes you do. Just be careful to not introduce changes to the templates and the routing.

For anyone still stumbling across this question, it's become easier to write unit tests for the controller with the introduction of Dependency Injection through controller function arguments.
Ex. if your code was written differently:
public function save(Request $request, DocumentManagerInterface $em, int $id): RedirectResponse
{
/* Actual code here
...
*/
}
Your UNIT test could simply do this:
public function testSave(): void
{
$em = $this->createMock(EntityManagerInterface::class);
// test calls on the mock
$controller = new XXXController();
$response = $controller->save($em);
// response assertions
}
Also note that if you are using a repository, you can inject the repository directly, given that you extend your Repository from the Service.
Tip: You might want to look at the Symfony best practices and use the ParamConverter instead of an $id (https://symfony.com/doc/current/best_practices/index.html)

I have discovered that a few short lines are all that's needed to get Symfony into a test harness:
// Load the autoloader class so that the controller can find everything it needs
//$loader = require 'app/vendor/autoload.php';
require 'app/vendor/autoload.php';
// Create a new Symfony kernel instance
$kernel = new \AppKernel('prod', false);
//$kernel = new \AppKernel('dev', true);
// Boot the kernel
$kernel->boot();
// Get the kernel container
$container = $kernel->getContainer();
// Services can be retrieved like so if you need to
//$service = $container->get('name.of.registered.service');
// Create a new instance of your controller
$controller = new \What\You\Call\Your\Bundle\Controller\FooBarController();
// You MUST set the container for it to work properly
$controller->setContainer($container);
After this code, you can test any public methods on your controller. Of course, if you are testing production code (as I have to; my development code works completely differently because the code base is written terribly) be aware that you might be touching databases, making web calls, etc.
However, the upside is that you can start doing code coverage of your controllers to understand why they're not working properly.

Related

PHPUnit gives error: Target [Illuminate\Contracts\View\Factory] is not instantiable

I created a simple test for my new Laravel 7 application. But when I run php artisan test I get the following error.
Target [Illuminate\Contracts\View\Factory] is not instantiable.
The error doesn't appear when I go to the page in the browser.
$controller = new HomeController();
$request = Request::create('/', 'GET');
$response = $controller->home($request);
$this->assertEquals(200, $response->getStatusCode());
Although "Just write feature tests" may seem like a cop-out ("They're not unit tests!"), it is sound advice if you do not want to get bogged down by framework-specific knowledge.
You see, this is one of those problems that come from using facades, globals, or static methods. All sorts of things happen outside of your code (and thus your test code) in order for things to work.
The problem
To understand what is going on, you first need to know how Laravel utilizes Containers and Factories in order to glue things together.
Next, what happens is:
Your code (in HomeController::home() calls view() somewhere.
view() calls app() to get the factory that creates Views1
app() calls Container::make
Container::make calls Container::resolve1
Container::resolve decides the Factory needs to be built and calls Container::build to do so
Finally Container::build (using PHP's ReflectionClass figures out that \Illuminate\Contracts\View\Factory can not be Instantiated (as it is an interface) and triggers the error you see.
Or, if you're more of a visual thinker:
The reason that the error is triggered is that the framework expects the container to be configured so that a concrete class is known for abstracts (such as interfaces).
The solution
So now we know what is going on, and we want to create a unit-test, what can we do?
One solution might seem to not use view. Just inject the View class yourself! But if you try to do this, you'll quickly find yourself going down a path that will lead to basically recreating loads of framework code in userland. So not such a good idea.
A better solution would be to mock view() (Now it is really a unit!). But that will still require recreating framework code, only, within the test code. Still not that good.[3]
The easiest thing is to simply configure the Container and tell it which class to use. At this point, you could even mock the View class!
Now, purists might complain that this is not "unit" enough, as your tests will still be calling "real" code outside of the code-under-test, but I disagree...
You are using a framework, so use the framework! If your code uses glue provided by the framework, it makes sense for the test to mirror this behavior. As long as you don't call non-glue code, you'll be fine![4]
So, finally, to give you an idea of how this can be done, an example!
The example
Lets say you have a controller that looks a bit like this:
namespace App\Http\Controllers;
class HomeController extends \Illuminate\Routing\Controller
{
public function home()
{
/* ... */
return view('my.view');
}
}
Then your test[5] might look thus:
namespace Tests\Unit\app\Http\Controllers;
use App\Http\Controllers\HomeController;
use Illuminate\Contracts\View\Factory;
class HomeControllerTest extends \PHPUnit\Framework\TestCase
{
public function testHome()
{
/*/ Arange /*/
$mockFactory = $this->createMock(Factory::class);
app()->instance(Factory::class, $mockFactory);
/*/ Assert /*/
$mockFactory->expects(self::once())
->method('make')
->with('my.view')
;
/*/ Act /*/
(new HomeController())->home();
}
}
A more complex example would be to also create a mock View and have that be returned by the mock factory, but I'll leave that as an exercise to the reader.
Footnotes
app() is asked for the interface Illuminate\Contracts\View\Factory, it is not passed a concrete class name
The reason Container::make does nothing other than call another function is that the method name make is defined by PSR-11 and the Laravel container is PSR compliant.
Also, the Feature test logic provided by Laravel already does all of this for you...
Just don't forget to annotate the test with #uses for the glue that is needed, to avoid warnings when PHPUnit is set to strict mode regarding "risky" tests.
Using a variation of the "Arrange, Act, Assert" pattern
This is not how you test endpoints in Laravel. You should let Laravel instantiate the application as it is already setup in the project, the examples you can see here.
What you already wrote can be rewritten to something like this.
$response = $this->call('GET', route('home')); // insert the correct route
$response->assertOk(); // should be 200
For the test to work, you should extend the TestCase.php, that is located in your test folder.
If you're finding this in The Future and you see #Pothcera's wall of text, here's what you need to know:
The ONLY reason he's doing any of that and the ONLY reason you're seeing this in the first place in a Unit test is because he and you haven't changed from PHPUnit\Framework\TestCase to Tests\TestCase in the test file. This exception doesn't exist when you extend the test case that includes app().
My advice would be to simply extend the correct base test case and move on with your life.

Correct unit testing

I started using unit and functionality test with this project and because of this I have some questions:
Im working with the symfony php framework. And I have a doctrine like LDAP ORM service.
Furthermore I have a user repository (as a service) which depends on the LDAP ORM service, the logger and a validation service.
Now I want to write a unit test for the addUser function of the UserRepo.
It will internally call: getNewUidNumber, userToEntities, doesUserExist and getUserByUid.
My question is:
Should I mock all these internal function to just test the addUser function? Would that be against the unit test idea (just test the API).
Or should I just mock the LDAP ORM service, the Logger, and the validation service, so that the class calls all internal functions? But this would cause a huge test function with a lot of mocking because I have to mock the repositories for all internal repositories calls.
Or should I start the symfony kernel and use the ServiceContainer to use the ORM LDAP service with a real test database. But wouldn't that be a functionally test and not a unit test?
I heard that its bad to have so many dependencies in a test. So I thought it would be bad to use the whole serviceContainer.
Adduser:
public function addUser(User $user)
{
$pbnlAccount = $this->userToEntities($user);
if(!$this->doesUserExist($user)) {
$pbnlAccount->setUidNumber($this->getNewUidNumber());
$this->ldapEntityManager->persist($pbnlAccount);
$this->ldapEntityManager->flush();
}
else {
throw new UserAlreadyExistException("The user ".$user->getUid()." already exists.");
}
return $this->getUserByUid($user->getUid());
}
For more code, like the internal functions:
https://gist.github.com/NKPmedia/4a6ee55b6bb96e8af409debd98950678
Thanks
Paul
First, I would like to rewrite the method a tiny bit, if I may.
public function addUser(User $user)
{
if ($this->doesUserExist($user)) {
throw new UserAlreadyExistException("The user ".$user->getUid()." already exists.");
}
// ... shortened for brevity
$pbnlAccount = $this->userToEntities($user);
$this->ldapEntityManager->persist($pbnlAccount);
}
The other relevant method is:
private function doesUserExist(User $user)
{
$users = $this->ldapRepository->findByUid($user->getUid());
return count($users) === 1;
}
Immediately we can see that we basically have two tests:
We test that the method throws when the user exists
We test that the method persists a PbnlAccount if the user does not exist.
If you do not see why we have these two tests, note that there are 2 possible "flows" in this method: one where the block inside the if statement is executed, and one where it is not executed.
Lets tackle the first one:
public function testAddUserThrowsWhenUserExistsAlready()
{
$user = new User();
$user->setUid('123');
$ldapRepositoryMock = $this->createMock(LdapRepository::class);
$ldapRepositoryMock
->method('findByUid')
->expects($this->once())
->with('123')
->willReturn(new PbnlAccount());
$userRepository = new UserRepository($ldapRepositoryMock);
$this->expectException(UserAlreadyExistException::class);
$userRepository->addUser($user);
}
The second test is left as an exercise for the reader :)
Yes you will have to do some mocking in your case. You wil need to mock the LdapRepository and LdapEntityManager both in this case.
Note 1: this code probably is not runnable, since I do not know the exact details of your code base (and I wrote this off the top of my head), but that is beside the point. The point is that you want to test for the exception.
Note 2:
I would rename your function to createNewPbnlAccountForUser(User $user) which is longer, but more descriptive of what it actually does.
Note 3:
I am not sure why you are returning $this->getUserByUid() since that seems redundant (you already have the User right there), so I am ommitting that case.
You need to mock ldapEntityManager and all repository services but not the internal function.And as you said, don't boot kernel in unit test. So, you should test all cases with success and throwing exception (make sure to check all behaviour)
If you want to perform a unit test, you should mock all collaborators.
Now, entity managers, ldap services and so on should not be mocked (read more here).
Moreover, if you happen to be in a situation where the Arrange part (set mocks, stubs, and so on) is painful and take "a lot" of the test, maybe this is a smell that your class has too many responsibility (is doing too much things).
That said, when I do unit test, I would like the test to fail only for an internal (to the class) reason, not because I've changed a collaborator line that messes up all my tests.

How to handle dependency injections in a full OO applications

I have a dilemma on class designing.
I'm doing my best to respect SOLID principles but I don't know how to handle dependency injection.
Here is my dilemma:
I read it is a bad practice to instantiate objects inside classes to avoid introducing dependency. So where should our dependencies be created in a full object application? In a special object which is only responsible for dependency instantiations? If yes, what is the name of this object and how to define it? Is it what we call "controller"?
This "controller", what is the right way to unit test it? Should we unit test it?
In a full POO application, how to avoid passing our objects (often the same) between classes? For example, a DB object, Log, ... In this way, we take the risk to have constructors with many parameters, don't we?
In order to illustrate my dilemma, I tried to create a use case.
I want to create a script (that I partially implemented below) that generates a file and print another one:
<?php
/**
* Generate a file, add it to the queue and print the next one
*/
class Script
public function run() {
#Generate file
$fileComputor = new FileComputer(...);
$file = $fileComputor->compute();
#Instantiate dependencies for printing
$db = new Db(new PdoAdapter());
$printerDriver = new Driver(new HttpRequest(new CurlAdapter()));
$log = new Log($db);
$queue = new Queue($db);
$statsUpdater = new StatsUpdater($db);
$monitor = new Monitor(new StatsdDriver(new SocketAdapter()));
#Add generated file to the queue
$queueModel->push($file);
#Print the next file on queue if existing
$printer = new Printer($printerDriver, $log, $queue, $monitor, $statsUpdater);
$printer->print();
}
}
class Printer {
protected $_driver;
protected $_log;
protected $_queue;
protected $_monitor;
protected $_statsUpdater;
/**
* $driver : Driver used to send documents to the printer
* $log : Log actions in database
* $queue : Handle the print queue
* $monitor : Send metrics to Statsd (to feed the graphit GUI)
* $statsdUpdater : Consolidate the statistics database
*/
public function __construct($driver, $log, $queue, $monitor, $statsUpdater) {
$this->_driver = $driver;
$this->_log = $log;
$this->_queue = $queue;
$this->_monitor = $monitor
$this->_statsUpdater = $statsUpdater;
}
public function print() {
if ($this->_queue->hasNext()) {
$file = $this->_queue->getNext();
$this->_driver->print($file);
$this->_log->log('File has been printed');
$this->_monitor->sendCount(1);
$this->_statsUpdater->increment();
}
}
}
?>
What do you think about this implementation?
Every feature we will want to plug into our Printer class will result into a new dependency to pass to the constructor (if for example we want to also generate a syslog, to measure time that takes the printer to process, etc).
In a close future, we will have between 10 and 15 parameters into the constructor call.
So where should our dependencies be created in a full object application? In a special object which is only responsible for dependency instantiations?
You have 2 options:
you create all the objects yourself, at the root of your application, i.e. in your front controller (index.php) for example. If you application is a bit big, that becomes quickly a hell.
you use a Dependency Injection Container. That object will be responsible of creating objects (and injecting them their dependencies in the constructor). Same here: you have to use/call the container only at the root of your application, i.e. in the front controller (index.php) for example.
If yes, what is the name of this object and how to define it? Is it what we call "controller"?
That's the Container. To give you an example, here is PHP-DI - Understanding DI.
You can use dependency injection on the controller (and I would recommend to do it): you can get dependencies in the controller's constructor (just like in any service). Some frameworks makes that difficult though (Symfony for example).
This "controller", what is the right way to unit test it? Should we unit test it?
No really. Some containers allow you to configure "Factories" to generate some objects.
For example, if creating the DBConnection object is complex, you could write a factory class, that has a method that creates the DBConnection object. So you could test the factory class. But I wouldn't say that's necessary.
In a full POO application, how to avoid passing our objects (often the same) between classes?
You should never pass instances around, because you should never call a constructor: all objects are constructed by the container.
So it becomes really simple: you write each class by taking dependencies in the constructor, and that's it. You don't care about dependencies and what those dependencies need.
For example, a DB object, Log, ... In this way, we take the risk to have constructors with many parameters, don't we?
Yes. You said you expect to have like 15-20 parameters in the constructor: that's not good at all.
You should generally try to have 2-3 parameters maximum. Having to many means that your class has too much responsibilities, it does to many things.
You can try splitting up the code of your class into several smaller/more targeted classes, or use events for example.
If we take your example, your printer could rather be like:
public function print($file) {
$this->driver->print($file);
$this->log->log('File has been printed');
$this->monitor->sendCount(1);
$this->statsUpdater->increment();
}
It makes more sense: a printer prints a file.
It's one dependency less (the queue). And then you can have a PrintQueueProcessor that watches the queues, takes the next file and calls the printer to print it.
The printer does one job (print a file), the queue processor does one job (unqueue files to print them).

How to set up database-heavy unit tests in Symfony2 using PHPUnit?

I am quite new to the world of testing and I want to make sure I am on the right track.
I am trying to setup unit tests in a symfony2 project using phpunit.
PHPUnit is working and the simple default controller tests work fine. (Yet this is not about functional testing but unit testing my application.)
My project relies heavily on database interactions though, and as far as I understand from phpunit's documentation, I should set up a class based on \PHPUnit_Extensions_Database_TestCase, then create fixtures for my db and work from there.
Yet, symfony2 only offers a WebTestCase class which only extends from \PHPUnit_Framework_TestCase out of the box.
So am I right to assume that I should create my own DataBaseTestCase which mostly copies WebTestCase, only difference being that it extends from \PHPUnit_Extensions_Database_TestCase and implements all its abstract methods?
Or is there another "built-in" recommended workflow for symfony2 concerning database-centric tests?
As I want to make sure that my models store and retrieve the right data, I do not want to end up testing the specifics of doctrine by accident.
I have never used the PHPUnit_Extensions_Database_TestCase, mostly because for these two reasons:
It doesn't scale well. If you set up and tear down the database for every single test and you have a application which relies heavily on the database, you end up creating and dropping the same schema over and over again.
I like to have my fixtures not only within my tests but also within my development database and some fixtures are even needed for production (initial admin user or product categories or whatever). Having them inside an xml which can only be used for phpunit doesn't seem right to me.
My way in theory...
I use the doctrine/doctrine-fixtures-bundle for fixtures (no matter what purpose) and set up the whole database with all fixtures. I then execute all tests against this database and make sure to recreate the database if a test changed it.
The advantages are that I don't need to set up a database again if a test only reads but don't change anything. For changes I have to do drop it and create it again or make sure to revert the changes.
I use sqlite for testing because I can set up the database, then copy the sqlite file and replace it with a clean one to bring back the original database. That way I don't have to drop the database, create it and load all fixtures again for a clean database.
...and in code
I wrote an article about how I do database tests with symfony2 and phpunit.
Although it uses sqlite I think one can easily make the changes to use MySQL or Postgres or whatever.
Thinking further
Here are some other ideas which might work:
I once read about a test setup where before you use the database you start a transaction (within the setUp method) and then use the tearDown to rollback. That way you don't need to set up the database again and just need to initialize it once.
My setup described above has the drawback that the database is set up every time phpunit is executed, even if you only run some unit tests with no database interaction. I am experimenting with a setup where I use a global variable which indicates if the database was set up and then within the tests call a method whcih checks this variable and initializes the database if it didn't happened yet. That way only when a tests needs the database the setup would happen.
One problem with sqlite is that it doesn't work the same as MySQL in some rare cases. I had an issue once where something behaved different in MySQL and sqlite causing a test to fail when in with MySQL everything worked. I cannot remember what it was exactly.
tl;dr:
If and only if you want to go the whole functional test route, then I recommend looking up Sgoettschkes's answer.
If you want to unit test your application and have to test code that interacts with the database, either read on or jump directly to symfony2 docs (There is a more up-to-date version also talking about testing with fixtures for symfony5)
There were certain aspects in my original question that make it clear that my understanding of the differences between unit testing and functional tests was lacking. (As I have written I want to unit test the application, yet was also talking about Controller test at the same time; and those are functional test by defintion).
Unit testing only makes sense for services and not for repositories. And those services can use mocks of the entity manager. (I would even go as far as to say: If possible, write services that only expect entities to be passed into them. Then you only need to create mocks of those entities and your unit-tests of your business logic become very straightforward.)
My actual use case for my application was pretty well reflected on the symfony2 docs on how to test code that interacts with the database.
They provide this example for a service test:
Service class:
use Doctrine\Common\Persistence\ObjectManager;
class SalaryCalculator
{
private $entityManager;
public function __construct(ObjectManager $entityManager)
{
$this->entityManager = $entityManager;
}
public function calculateTotalSalary($id)
{
$employeeRepository = $this->entityManager
->getRepository('AppBundle:Employee');
$employee = $employeeRepository->find($id);
return $employee->getSalary() + $employee->getBonus();
}
}
Service test class:
namespace Tests\AppBundle\Salary;
use AppBundle\Salary\SalaryCalculator;
use AppBundle\Entity\Employee;
use Doctrine\ORM\EntityRepository;
use Doctrine\Common\Persistence\ObjectManager;
class SalaryCalculatorTest extends \PHPUnit_Framework_TestCase
{
public function testCalculateTotalSalary()
{
// First, mock the object to be used in the test
$employee = $this->getMock(Employee::class);
$employee->expects($this->once())
->method('getSalary')
->will($this->returnValue(1000));
$employee->expects($this->once())
->method('getBonus')
->will($this->returnValue(1100));
// Now, mock the repository so it returns the mock of the employee
$employeeRepository = $this
->getMockBuilder(EntityRepository::class)
->disableOriginalConstructor()
->getMock();
$employeeRepository->expects($this->once())
->method('find')
->will($this->returnValue($employee));
// Last, mock the EntityManager to return the mock of the repository
$entityManager = $this
->getMockBuilder(ObjectManager::class)
->disableOriginalConstructor()
->getMock();
$entityManager->expects($this->once())
->method('getRepository')
->will($this->returnValue($employeeRepository));
$salaryCalculator = new SalaryCalculator($entityManager);
$this->assertEquals(2100, $salaryCalculator->calculateTotalSalary(1));
}
}
No test database required for those kind of test, only mocking.
As it is important to test the business logic, not the persistence layer.
Only for functional tests does it make sense to have its own test database that one should build and tear down afterwards, and the big question should be:
When do functional test make sense?
I used to think that test all the things is the right answer; yet after working with lots of legacy software that in itself was barely test-driven developed I have become a bit more lazypragmatic and consider certain functionality as working until proven otherwise by a bug.
Assume I have an application that parses an XML, creates an object out of it, and stores those objects into a database. If the logic that stores the objects to the database is known to work (as in: the company requires the data and is, as of yet, not broke), and even if that logic is a big ugly pile of crap, there is no imminent need to test that. As all I need to make sure that my XML parser extracts the right data. I can infer from experience that the right data will be stored.
There are scenarios where functional test are quite important, i.e. if one were to write an online shop. There it would be business critical that bought items get stored into the database and here functional tests with the whole test database make absolute sense.
You can use this class:
<?php
namespace Project\Bundle\Tests;
require_once dirname(__DIR__).'/../../../app/AppKernel.php';
use Doctrine\ORM\Tools\SchemaTool;
abstract class TestCase extends \PHPUnit_Framework_TestCase
{
/**
* #var Symfony\Component\HttpKernel\AppKernel
*/
protected $kernel;
/**
* #var Doctrine\ORM\EntityManager
*/
protected $entityManager;
/**
* #var Symfony\Component\DependencyInjection\Container
*/
protected $container;
public function setUp()
{
// Boot the AppKernel in the test environment and with the debug.
$this->kernel = new \AppKernel('test', true);
$this->kernel->boot();
// Store the container and the entity manager in test case properties
$this->container = $this->kernel->getContainer();
$this->entityManager = $this->container->get('doctrine')->getEntityManager();
// Build the schema for sqlite
$this->generateSchema();
parent::setUp();
}
public function tearDown()
{
// Shutdown the kernel.
$this->kernel->shutdown();
parent::tearDown();
}
protected function generateSchema()
{
// Get the metadatas of the application to create the schema.
$metadatas = $this->getMetadatas();
if ( ! empty($metadatas)) {
// Create SchemaTool
$tool = new SchemaTool($this->entityManager);
$tool->createSchema($metadatas);
} else {
throw new Doctrine\DBAL\Schema\SchemaException('No Metadata Classes to process.');
}
}
/**
* Overwrite this method to get specific metadatas.
*
* #return Array
*/
protected function getMetadatas()
{
return $this->entityManager->getMetadataFactory()->getAllMetadata();
}
}
And then you can test your entity. Something like this (assuming you have a entity User)
//Entity Test
class EntityTest extends TestCase {
protected $user;
public function setUp()
{
parent::setUp();
$this->user = new User();
$this->user->setUsername('username');
$this->user->setPassword('p4ssw0rd');
$this->entityManager->persist($this->user);
$this->entityManager->flush();
}
public function testUser(){
$this->assertEquals($this->user->getUserName(), "username");
...
}
}
Hope this help.
Source: theodo.fr/blog/2011/09/symfony2-unit-database-tests

Unit Testing Zend Controller and Mocking some of the performed actions

I'm writing some unit tests (PHPUnit 3.6) for my controllers and want to verify that the correct actions etc.. are being fired. This is easy enough. However some of the controllers also perform certain actions via models that are undesirable such as inserting records into a database.
I am aware I need to mock these but am unclear how to do this. Taking the following example controller (cut down for clarity):
public function addAction()
{
$data = $this->getRequest()->getPost();
$model = $this->getModelFactory()->getCompetitionModel()->insert($data); }
}
Note, all I want to do is verify that the correct controller and action have been dispatched but do not want the record actually inserted. Likewise I have equivalents for delete etc.. I don't want records actually deleted.
What actually needs mocking here? The competition Model, the database adapter, or the model factory, or all three? How do I inject these? I have tried (again cut down for brevity):
public function testAddActionIsDispatched()
{
$this->request->setMethod('POST');
$this->request->setPost(array($data…));
$modelMock = $this->getMockBuilder('Competition_Adder')
->disableOriginalConstructor()
->getMock();
$factoryMock = $this->getMockBuilder('ModelFactory')
->disableOriginalConstructor()
->getMock();
// Configure the stub.
$factoryMock->expects($this->any())
->method('getCompetitionModel')
->will($this->returnValue($modelMock));
$modelMock->expects($this->once())
->method('insert')
->will($this->returnValue(true));
$this->dispatch('/mymodule/add/');
$this->assertController('test');
$this->assertAction('add');
$this->assertResponseCode(200);
}
}
It was my understanding that PHPUnit magically substituted any references to the originals with the mocks so that when the dispatch was called the fake mocks are used in their place. This isn't happening. Can someone please clarify how this is achieved?
It looks like your mocks are set up correctly. I actually didn't know you could return mocks from mocks until I saw your question and researched it a bit.
What's happening here is that you need to make the method getModelFactory() return an instance of your mock factory. Right now it just returns the real thing.
I'm not sure what happens in your getModelFactory method, so it is hard for me to say how you could override it to make it return your mock factory.
But maybe you don't have to override it. In my ZF app, I don't test controllers, but to test stuff that requires saving stuff to my models, I just change to a test database in my configuration file for testing. I use Doctrine1.2, so I just start a transaction in the setUp() method and a rollback in the tearDown method().
My test database is completely empty, and I basically create the necessary data in each test method with some test specific factory classes. The drawback is it does seem to use a lot of memory. I think it hits 200MB on about 140 tests and not all of these require database access.
I just use this method since it is the easiest for me to implement since I only had to change the database config. If you're not working a very large scale project, this may work for you. You could also run your tests against a test database using sqlite in memory, which should work for you since are not testing the database in your test. The data just gets inserted and then at the end of test, it's gone. In my project, I use a MySQL test database, because I wanted it be as close to what is in production as possible.
Example (You're probably not using Doctrine. I'm just illustrating how I use transactions and rollback to keep my test database in a consistent state):
public function setUp()
{
$this->bootstrap = new Zend_Application(
APPLICATION_ENV, APPLICATION_CONFIG);
parent::setUp();
$bootstrap = $this->bootstrap->getBootstrap();
$this->_conn = Doctrine_Manager::connection();
$this->_conn->beginTransaction();
}
public function tearDown()
{
$this->_conn->rollback();
$this->_conn->close();
}
Yep, check out the previous answer. I can agree in all cases. But the same thing can't be implemented with MySQL and Zend_Db. That's because Zend_Db doesn't have nested transactions.
So the only thing you can do is to use test database and rebuild it after each tests.
Check out how it is done via Codeception testing framework with Zend Framework and Db module.

Categories