I'm trying to set up a testing suite for my Symfony 3 app, and I'm wondering what the correct method for setting up the test database is. I've found this article, but it doesn't actually explain how to add fixtures programmatically.
Also, it appears their example sets up the test database for every test.
Is there a way to setup a test database which is automatically loaded with fixtures when phpunit is run? The official documentation is kind of sparse
Symfony has different environments you can operate in. By default those are prod(production), dev(developement) and test. Although it may not be exactly what you want, you can configure different config, paramaters, routes and so on for each environment. Read the official documentation for more info but yeah, you can setup your parameters.yml file for test mode which could have a different database configured there.
https://symfony.com/doc/current/configuration/environments.html
If you extend your TestCases from KernelTestCase or WebTestCase, basically extend from \PHPUnit_Framework_TestCase you have methods like setUpBeforeClass() or setUp() which are run by PhpUnit before your test/test cases is/are executed.
Use this to e.g. create your fixtures, load your SQL file or whatever and however you might require your prerequisites for your tests.
Related
Is there a thorough documentation for different commands for writing phpunit tests with Laravel?
All I can find are introduction-level docs like https://laravel.com/docs/5.3/application-testing, but nothing that would give more info about the available methods and their parameters.
Also, I haven't found a way to extend the test files beyond the default
php artisan make:test SomeTest
At least I would like to change the testExample() function name to something more describing and add several functions in the test file.
This question: Run PHPUnit Tests in Certain Order has an accepted answer which I agree with, but the design problem is with PHP and PHPUnit.
The project I'm testing uses ZF2 and Doctrine. The AbstractHttpControllerTestCase has a method "dispatch" which instantiates a ZF2 Application and goes through all the steps of creating the Response object. These tests are annotated with #covers to ensure other methods aren't covered by running requests during a test. The requests may involve view scripts which invoke view helpers which use all sorts of services, so it becomes infeasible to mock all services used during a given request (and this code would become tedious to copy and maintain for each test).
PHPUnit has the ability to run tests in a separate process, it does this by forking a new PHP instance and feeding it compiled code templates (weird stuff). It will then include all files listed by get_included_files(), which includes everything that ever hit the autoloader. Even with preserveGlobalState disabled, it will still include everything that's been touched by all prior tests in the new process.
Some of the dependencies (installed through composer) use static methods, classes marked final or both. Static methods can be mocked by PHPUnit, final classes have to be overloaded using Mockery as PHPUnit will flat out refuse to create mock objects of final classes. Overloading classes and functions (using the namespace trick) must be done in separate processes as to not influence subsequent tests. So far, so good.
Enter a test which overloads a dependency to set expectations on static methods (on a class which may or may not be marked final), or to set expectations on objects which are not yet instantiated. This will only work if none of the prior tests have ever touched the class to overload and set expectations on, or it'll fail with a "cannot redeclare class" error. PHPUnit has tried to be helpful and included everything to recreate the test environment in the subprocess, but as a result ruins the test case.
Therefore, it would be incredibly useful to mark tests with e.g. "#group isolated" and have those tests run before any other tests without having to invoke PHPUnit twice (besides the inconvenience of it, it would ruin coverage analysis).
Alternatively, if there's a way to override a class that already exists in PHP 5.5, that would allow the stricken test case to fix its precondition. But that's probably not going to happen (runkit is not an acceptable answer in any case).
Using Symfony\Bundle\FrameworkBundle\Test\WebTestCase, we get easy access to container, entity manager etc. We can use it for functional testing by automatic manual HTTP requests.
Can we use it to test Symfony2 console commands as well so that we can have easy access to container and all services?
I want to test my costum Symfony2 console commands which uses many services which in turn uses doctrine entity manager to access data.
PHPunit documentation suggest to extend the test class with PHPUnit_Extensions_Database_TestCase,
Can we extend WebTestCase instead of test instead to test console commands ?
I have already refereed
How to test Doctrine Repositories Cookbook
How to test code that interacts with the Database Cookbook
The Console Component Docs
WebTestCase is meant for functional testing your web applications. Nothing will stop you from using it to test commands, but it doesn't feel right (hence your question).
Testing commands
Remember, that command tests (as well as controller tests) shouldn't be complex, just like the code you're putting in there shouldn't be complex either.
Treat your commands as controllers, make them slim and put your business logic where it belongs - to the model.
Accessing the container
Having said that, you can implement your own KernelAwareTestCase (or ContainerAwareTestCase) yourself. Here's a base class I'm using occasionally: jakzal / KernelAwareTest.php Gist
Also, note that next to Symfony\Component\Console\Application there's a Symfony\Bundle\FrameworkBundle\Console\Application which can actually work with the Symfony kernel.
Final note
Remember, that the most extensive testing should be done on a unit level.
My Yii app getting phpunit coverage in controllers, models, components, modules but not any views. The problem is that code coverage loader in phpunit includes the view file during preparing a coverage report. Views with forms have calls $this->beginWidget which causes a crash since there is no $this context.
Views dont really have important code or logic but still they have some conditions and even loops to call renderPartial so it would be good to get a view code also covered.
Is there a solution to this problem?
Have you tried extending CWebTestCase? Generally when writing unit tests, you have fixtures and things to provide the necessary data - but with tests on views and 'functional' tests, for web-apps, it's generally easiest to mimic a browser and have it perform actions on the web app as if a user was actually using it. Currently, this mimicking is most easily done with Selenium (in my opinion).
The Yii Guide on Functional Testing is a good place to start as well as the Selenium Documentation. There's also this book that goes over using selenium (I'm not sure if the newest edition does, but I know the previous release with Publication Date: August 11, 2010 did), and Larry Ullman's Yii Book will have chapters on testing and the usage of Selenium in functional tests when he's completed that chapter.
Hope this helps!
Update to further explain CWebTestCase
CWebTestCase extends PHPUnit_Extensions_SeleniumTestCase which directly implements a functional testing foundation that you can use within phpunit in order to test views, test widget creation inside of views, assert that text exists, 'click' on links, etc. These tests are still run from the command line though they require that Selenium-RC server be started upon the test being run and they require a valid browser being configured. A valid browser can be configured with as little code as the following placed inside of the setUp() function:
$this->setBrowser('*firefox /usr/lib/firefox/firefox-bin');
Stating that code coverage cannot be provided by CWebTestCase is not true, as CWebTestCase extends PHPUnit_Extensions_SeleniumTestCase, which provides the following as quoted from the documentation:
PHPUnit_Extensions_SeleniumTestCase can collect code coverage
information for tests run through Selenium:
Copy PHPUnit/Extensions/SeleniumTestCase/phpunit_coverage.php into
your webserver's document root directory. In your webserver's php.ini
configuration file, configure
PHPUnit/Extensions/SeleniumTestCase/prepend.php and
PHPUnit/Extensions/SeleniumTestCase/append.php
as the auto_prepend_file and auto_append_file, respectively. In your test
case class that extends PHPUnit_Extensions_SeleniumTestCase, use
protected $coverageScriptUrl = 'http://host/phpunit_coverage.php';
to configure the URL for the phpunit_coverage.php script.
What are the current best practices for testing database interaction with Symfony2? I have a simple CRUD setup and i want to make sure my testing is OK. Right now, i have 4 tests, each one making sure that create, update, delete and list actions are occurring ok.
I have two magic methods, __construct and __destruct, on my test case. Inside them, i call exec() with 'php app/console ...' in order to create the database, create the schema and later on drop the database. However, this is SLOW as hell and it happens all the time when i have more than one test case.
How should i proceed when it comes to database testing and isolating such tests?
I think it's best to always start clean to be sure that tests are fully isolated. To do that I'm simply building the database structure before each test and than I'm filling it with fixtures needed for a given test.
Note that I'm only building needed database tables and I'm only inserting needed fixtures. It's a bit faster than loading a big database dump. It's also more clean as tests don't share fixtures (which makes them easier to maintain).
I have a base test case class called KernelAwareTest which helps me in building the schema. You can find it on gist: https://gist.github.com/1319290
setUp() boots the Symfony kernel and stores a reference to it in a class property (together with references to the DIC and the entity manager). Also a call to generateSchema() is made to generate the database schema (it uses Schema Tool from Doctrine).
By default it generates the database structure for all entities known to the entity manager. You can change this behaviour in your test class by overriding the getMetadatas() method.
P.S.: I tried using in memory database (sqlite) but it wasn't perfect. Anyway it's probably better to run tests against the database you use on production.
Database testing is always slow as you need to create/drop your schema before/after each test. To avoid unnecessary operations, you could:
create schema in the 'setUpBeforeClass' method;
ensure your 4 tests are launched in one thread with the '#depend' annotation;
drop schema in the 'tearDownAfterClass' method.
The schema will be created/droped only once for your tests case.
You can also setup doctrine to use an inmemory sqlite database (which is very fast):
doctrine:
dbal:
driver: pdo_sqlite
path: :memory:
memory: true
Anyway, '_construct' and '_destruct' should never be used in phpunit test cases, instead you should use 'setUp' and 'tearDown'.
The question is pretty old but still valid today so here is my experience and how I handle it today on my Symfony projects.
I started of using an SQLite in-memory database for my tests and I rebuild the db schema + inserted fixtures before each single test case. This had two major drawbacks:
It was still way too slow :(
On dev & prod I used Mysql which soon became a problem because SQLite simply does not have all the features needed and sometimes behaves differently
Using MSQL for the tests and rebuilding the schema + inserting fixtures before each test was simply too slow. So I was looking for alternatives...
I stumbled across this blog post: http://alexandre-salome.fr/blog/Symfony2-Isolation-Of-Tests
The guy here suggests to run tests inside active database transactions and simply roll back any changes after every single test.
I took this idea and created a bundle for it: https://github.com/dmaicher/doctrine-test-bundle
The setup of the bundle is really easy and does not require changing any existing php test classes. Internally it changes the doctrine config to use custom database connections + driver.
With this bundle you can simply create your database schema + insert fixtures ONCE BEFORE running the whole testsuite (I prefer doing this in a custom phpunit bootstrap file). Using the phpunit listener all tests will run inside database transactions.
I've been using this bundle since a while already and for me it works out perfectly using SQLite, MySQL or PostgreSQL.
Since a while its also used on the symfony-demo project.
testing on local machine is pain in the ... ,so i'm started to using ci system buddy.works (there is free stand-alone version) , and for this i neeeded to resolve this issue on my own.
result is :
all tests works
tests are runing on production sql data
tests are running in separation (not in dev or production) - so i can do
anything that i want with database
all pushes to git are tested and i have notification if something is broken
all pushes/pull request to deploy branch are automatic uploaded to production
This is my solution :
second parameters.yml in config with configuration for test
on production i'm making daily sqldump
on starting test on ci this sql backup is copied via scp to test machine
to prepare all this i'm using robo.li ( my config is below)
/**
* This is project's console commands configuration for Robo task runner.
*
* #see http://robo.li/
*/
class RoboFile extends \Robo\Tasks
{
function loadDb(){
$this->taskExecStack()
->stopOnFail()
->exec(" mysql -h mariadb -u root -pqwerty -e 'create database test' ")
->exec(" mysql -h mariadb -u root -pqwerty test < test.sql ")
->run();
}
function prepareDb(){
$this->taskExecStack()
->stopOnFail()
->exec("cp app/config/parameters-test.yml app/config/parameters.yml")
->run();
$this->taskReplaceInFile('app/config/parameters.yml')
->from('database_host: 127.0.0.1')
->to("database_host: 'mariadb'")
->run();
$this->taskReplaceInFile('app/config/parameters.yml')
->from('database_user: dbuser')
->to("database_user: 'root'")
->run();
$this->taskReplaceInFile('app/config/parameters.yml')
->from('database_password: 123')
->to("database_password: 'qwerty'")
->run();
}
}
i hope it help you to create idea how to organize all this . Using stand alone ci is difficult to setup , but it's really good idea