Donnerstag, 12. April 2012

Testclasses for symfony2

So, when developing with symfony2, I rely on my tests. They are my safety net and without them, I get a little nervous after every change. Does everything work? Did I forget anything? So I developed some classes which I extend. They work on top of PHPUnit and the symfony2 WebTestCase. The classes are used by my different types of Tests: UnitTests, ValidationTests, FunctionalTests (as well as IntegrationTests).

UnitTestCase


For my UnitTests, I use the following class:

abstract class UnitTestCase extends \PHPUnit_Framework_TestCase {

}

One might argue that it isuseless, which is true to some dagree. As I have some hundred files with UnitTests, I want to encapsulate PHPUnit so I can add missing or change existing behaviour. If PHPUnit gets updated and something general changes, I can emulate the old functionality with this class. Also, it's not that much overhead.

ValidationTestCase

For my validation tests I need many different classes which are very hard to mock. I tried doing UnitTests, but to me thats too much overhead. I agree that it's possible, but for my setup I don't see the benefits right now. So this is my class for Validators:

use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;

abstract class ValidatorTestCase extends WebTestCase {

    protected $container;
    protected $validator;

    public function setUp() {
        $kernel = self::createKernel();
        $kernel->boot();

        $this->container = $kernel->getContainer();
        $this->validator = $this->container->get('validator');
    }

}

As you can see, I boot the application using the createKernel from the WebTestCase. I store the container and the validator for future use. My TestClass now simply needs to pass the validator a value and the object and test for the returned errors. Pretty easy!

FunctionalTestCase

This class is used for my functional tests where I need to access the database. I also use this for IntegrationTests as the setup is the same. I got some of this ideas on this blog. There was also another blog which enspired this class, but I cannot remember which one it was.

use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Input\ArrayInput;

abstract class FunctionalTestCase extends WebTestCase {

    protected $client;
    protected $entityManager;

    protected static function initialize() {
        self::createClient();
        $application = new Application(static::$kernel);
        $application->setAutoExit(false);

        self::createDatabase($application);
    }

    private static function createDatabase($application) {
        self::executeCommand($application, "doctrine:schema:drop", array("--force" => true));
        self::executeCommand($application, "doctrine:schema:create");
        self::executeCommand($application, "doctrine:fixtures:load", array("--fixtures" => __DIR__ . "/../DataFixtures/ORM/test"));
    }

    private static function executeCommand($application, $command, Array $options = array()) {
        $options["-e"] = "test";
        $options["-q"] = null;
        $options = array_merge($options, array('command' => $command));
        return $application->run(new ArrayInput($options));
    }

    public function setUp() {
        $this->populateVariables();
    }

    protected function populateVariables() {
        $this->client = static::createClient();
        $container = static::$kernel->getContainer();
        $this->entityManager = $container->get('doctrine')->getEntityManager();
    }

}

Alright, we have some code here. Let me walk you through it. I have a static initialize, which sets up the database by executing command line commands. I call this mostly in my TestClass within setUpBeforeClass. I also got populateVariables which creates a client and populates the $entityManager for future use. It's called within setUp. With this class, I can easily do functional tests on a clean database. As I have a static function which does set it up, I can call it whenever I need one. Of course this is before I execute the first test on a new class, but also when I change data which gets in the way of the next test.

The End 

Of course this three classes are not the end. I have one class in development for easy repository testing. I also think about some form testing classes. The classes itself can also be improved. They depend on the project they are used in and the way you write tests. If you have some notes on improvements, don't hesitate to drop me some lines!

Keine Kommentare:

Kommentar veröffentlichen