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
Related
I've created a package that manipulates eloquent models, and I've created test cases for it.
The package is supposed to call save() or delete() for the models, at some point, which would attempt to hit the database.
Actual Database approach
As first attempt I tried this approach and also this. The point is that in the context of a package (even using orchestra/testbench) I'd need to configure a database and migrations. Since the package itself does not have any model (but I've created a dummy model for test purposes) I see this approach as possibly overkill. In any case, I'd still accept to setup a prepared sqlite db in memory, which I also tried but could not make it work (It was attempting to use forge connection instead sqlite and I could not make it access another connection. I may provide details on what I did for it).
Mocking approach
Another possible attempt (as per my limited understanding on this) is to partially mock the model. But after I mocked it, it would not know how to handle other calls such as fill(), for which I was willing the common behavior, but I received a method not found exception.
Method override approach
Given that these two possible attempts went bad, I defaulted to a third possible approach which worked for me, but I'm honestly not sure if this is the way to go about it.
In order to avoid test failure due to lack of database when calling save() or delete() methods, I overrided them (full source code here) :
class DummyContact extends Model
{
// ...
public function save(array $options = [])
{
$this->exists = true;
$this->wasRecentlyCreated = true;
}
public function delete()
{
$this->exists = false;
$this->wasRecentlyCreated = false;
}
}
This way, I'm able to test the following code (full source here):
public function unifyOnBase()
{
$mergeModel = $this->merge();
$this->modelA->fill($mergeModel->toArray());
$this->modelA->save();
$this->modelB->delete();
return $this->modelA;
}
So my question is, is this approach acceptable? (I think it is fair, I don't see exceptional pitfalls, but I suspect there exist more elegant approaches). In case there are suggested approaches, like Mocking or using an actual DB for running the tests, I'd like to know what adaptations should I do to my testing codebase for taking them.
Final but important note: I'm not willing to test models itself, I'm willing to test my code that uses (and thus depends on models).
Thanks to comment of Jonas Staudenmeir:
An integration test with an actual database is the most thorough way to test your code. IMO, you should at least have integration tests for the fundamental features. This is how I use an SQLite database for the tests of my package.
I could pair and get working the in-memory sqlite approach.
Removed override functions
Added Capsule test case setUp code as per this working project as example (Thanks for that)
In my case I also required to install the sqlite driver sudo apt-get install php7.2-sqlite
Tests now still run successfully, while the solution looks more elegant and cleans-up the function override workarounds which would easily break upon API upgrades of Eloquent Models. This would also enable easier access to testing relationship-dependent features of the package.
I think your first proposal, the actual database approach is the best - using the package orchestra/testbench.
Since you have no Eloquent models in your package, but your package modifies Eloquent models, I think you should create an Eloquent model only for testing purposes inside your test folder.
For example, put the DummyContact into tests/Models/DummyContact.php and put the migration file into tests/Database/Migration/DummyContactMigration.php.
Now all you need to do, is to setup a basic TestCase.php.
Make sure to explicitly call the migration file of your DummyContact model.
Here is an example:
<?php
namespace MyVendor\MyPackage\Tests;
use MyVendor\MyPackage\MyServiceProvider;
class TestCase extends \Orchestra\Testbench\TestCase
{
public function setUp(): void
{
parent::setUp();
$this->loadMigrationsFrom(__DIR__ . '/database/migrations');
$this->artisan('migrate', ['--database' => 'testbench'])->run();
}
/**
* add the package provider
*
* #param $app
* #return array
*/
protected function getPackageProviders($app)
{
return [MyServiceProvider::class];
}
/**
* Define environment setup.
*
* #param \Illuminate\Foundation\Application $app
* #return void
*/
protected function getEnvironmentSetUp($app)
{
// Setup default database to use sqlite :memory:
$app['config']->set('database.default', 'testbench');
$app['config']->set('database.connections.testbench', [
'driver' => 'sqlite',
'database' => ':memory:',
'prefix' => '',
]);
}
}
You may also want to read this blog post
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.
I recently tried to improve my unit testing skills and read quite some literature about unit testing and I am also trying to realize what I learned in a php-project I am currently developing with phpunit. But I still have a in my opinion very fundamental question how to unit test methods which interact with objects of other classes or even with other methods of the same class.
Is there some rule of thumb or some help how I can decide what dependencies I should stub/mock and for what dependencies I should simply use a normal object? To clarify my question, here is an example code, with different scenarios:
interface DependencyInterface {
public method dependentMethod() { ... }
}
class Dependency implements DependencyInterface {...}
class ClassUnderTest {
private $dependency
public __construct(DependencyInterface $dependency) {
$this->dependency = dependency;
}
public function methodUnderTest() {
...
$result1 = $this->dependency->dependentMethod();
...
$result2 = $this->otherMethod();
...
$result3 = $this->usedInMultiplePublicMethods();
}
public function otherMethod() {...}
private function usedInMultiplePublicMethods() {...}
}
So my questions are now for a unit test which tests the functionality of the method methodUnderTest, should I:
stub the interface DependencyInterface and inject it in the constructor, or should I simply use an instance of the implementation Dependency?
partially stub the class ClassUnderTest itself to deliver a fixed result for otherMethod, since this maybe very complex method has already its own complete unit tests?
I decided to not unit tests private methods, since they are not part of the interface of the class (I know this is a controversial topic and is not the scope of my question). Do I have now to cover for each public method which uses the private method usedInMultiplePublicMethods all possible effects which may occur in the private method? Or should I only test all possible effects in one of the public methods which uses it and stub the private method in the tests for all other public methods?
I am quite not sure about when to use stubbing/mocking and when not.
The why of mocking is to be able to write unit test that means a test which is: fast, isolated, repeatable, self validating and Thorough and Timely (F.I.R.S.T)
To be able to test a unit/module in isolation you may need to mock/stub any external module (database access, api call, logging system...).
For your points 1 & 2 , rad's answer points to main underlying principles to keep in mind e.g. if you are going to test a logic which is using a database service to fetch data and then doing computations on fetched data, would you mock that database service or use real database service ?
As is clear from objective - you are unit testing logic itself and not database service data fetch so you would mock database service and would assume that database service is giving correct data & you would simply focus on testing logic on computed data. You will have separate tests for database fetching service and that isolation property is all about.
The word unit is significant in that sense that your these tests should be very focused on current logic only by limiting your scope & by not cluttering everything else into it.
This answer is mainly for your points # 3. Its OK to not explicitly test private methods but if you go by basic purpose of unit tests - you wouldn't be much worried about something being private or public. Somewhere down the line, unit testing is for developer self satisfaction too & it would simply make your code more robust if unit tests are written for private methods too.
Just because access level is private doesn't change the basic concept that its a piece of logic that needs testing. Code coverage wise , you might be OK from one public method but I am of view that you should treat calls from different public methods as distinct.
Never forget the basic purpose of unit tests - that you are trying to find errors in your logic, trying to cover all boundary cases & trying to make your code more robust.
I have a test for testing data model:
<?php
namespace Tests\Model\SQL;
/**
* #group Model_Users
* #group Model
*/
class UsersTest extends TestCase {
/** #var Users */
private $model;
public function setUp() {
parent::setUp();
$this->connection->execute( file_get_contents(
__DIR__ . '/fixtures/countries.sql' ) );
$this->model = new \Users(
$this->connection
);
}
public function testUserExists() {
$exists = $this->model->userExists( 'johndoe' );
$this->assertTrue( $exists );
}
}
The Tests\Model\SQL\TestCase uses SQLite as a database to keep tests as fast as possible.
Anyway, as you may expect, SQLite is not the same database as Postgres, therefore there could be some specialites, which will fail on SQLite and pass on PostgreSQL and vice versa.
My question is:
How to effectively (when keeping DRY in mind) design the architecture to be able to run tests only with SQLite and then only with Postgres.
When I am saying DRY, I mean ideally case, when I write only one testcase for one model and the architecture of my tests will take care of that, so I will be able to run tests like this:
php tests/run.php --group=MockedDbWithSqlite # fast way, prefered, default
php tests/run.php --group=RealDb # slower way, but with more precise results
So judging from the comment the issue is rather independent of any concrete storage issue and more about how one can pass a custom parameter to PHPUnit.
Because, using this parameter, you'd switch out DB drivers.
There are a couple of options to do this but no officially supported "custom parameters" objects or something. Even so it would be nice if someone would add this maybe :)
My suggestion would be to use one of the following to options:
Env variables
Make the test/bootstrap code rely on $_ENV variables.
$dbDriverFactory->getInstanceFor(getenv('DB'));
and using one of the ways below to
export DB=postgress
phpunit
or
DB=postpress; phpunit;
or using phpunits xml config to set the env or whatever you like.
By doing so you could provide two xml configs phpunit-sqlite.xml & phpunit-pg.xml and run:
phpunit -c phpunit-sqlite.xml
It doesn't have to be env. Any of the methods listed in the doc section Setting PHP INI settings, Constants and Global Variables will work.
"Custom" cli parameters
Calling phpunit like this:
phpunit --colors --usePostgres
and adding something like this in test cases or bootstrap code:
if (in_array('--usePostgress', $_SERVER['argv'], true) {
$db = $this->setupPG();
} else {
$db = $this->setupSqlite();
}
I read this differently to edorian. I like to use inheritance for this kind of thing. The advantage is you can run both SQLite and Postgres tests in the same test run. The disadvantage is you have to add some code to each test file where you want to run with both databases. And you have to repeat this for each new database you want to test. Here is how it works:
class UsersTestBase extends TestCase {
//Unchanged
}
class UsersTestSQLite extends UsersTestBase{}
class UsersTestPostgres extends UsersTestBase {
function __construct(){
parent::_construct("postgres");
}
}
So, UsersTest is exactly the same, except the name has changed to append "Base".
You then have a derived UsersTestSQLite class. This does nothing. At this point we've not gained or lost anything.
Then we had UsersTestPostgres. This somehow tells the object to use postgres instead of sqlite. In this case I've assumed TestCase has a constructor that can take the database to use as an optional parameter (with the default being "sqlite"). Obviously there are lots of alternatives to that, but I hope you see the general idea.
Finally you need to add the suitable #group tags to the derived classes, to allow you control over when they are run.
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.