Symfony 2 test with your own created service

Symfony 2 functional test with login authentication, data base connection and instqntiatiate your custom Service in Symfony 2.

Functional Test are Basically:

  • Make a request
  • Test the response
  • Click on a link, fill field values, submit a form
  • Test the response
  • Rinse and repeat

Unit tests

To test a specific PHP class only, not browser actions

Do not write unit test for a Controller

Skeleton Generator

Create an empty skeleton of the class with all the methods you are going to test.

https://github.com/sebastianbergmann/phpunit-skeleton-generator

Create a skeleton class

php phpunit-skelgen.phar generate-test --bootstrap="app/bootstrap.php.cache" MyClass

I Had MyClass.php copied at the same root folder as the file phpunit-skelgen.phar and deleted all the includes and imports before the code: class MyClass {

}

Sample code for a functional test with login action

If the functional test is in a secured area, you will need to implement the login function. So, here is how it goes: 1. Login to secure area 2. Browse to a form to update an article 3. Update article values 4. Submit form 5. Assert that we are back to article list page


class ArticleControllerTest extends WebTestCase { private $client = null;
/**
 * Test Update Article
 */
public function testArticle()
{                
    $this->logIn();
    $crawler = $this->client->request('GET', '/admin/article/1');

    $this->assertEquals(
        $this->client->getResponse()->getStatusCode(),
        200
    );

    $this->assertTrue(
        $crawler->filter('html:contains("Update Article")')->count() == 1
    );


    // A submit button with "Update" value
    $form = $crawler->selectButton('Update')->form();

    $form['Article[type]'] = 'car';
    $form['Article[price]'] = 10.000;

    $crawler = $this->client->submit($form);

    // Back to list page after submit
    $this->assertTrue(
        $crawler->filter('html:contains("Article List")')->count() == 1
    );        
}

/**
 * Login Session 
 */
 private function logIn()
 {   
    $this->client = static::createClient(array(), array('HTTP_HOST' => 'myhost.com'));
    $this->client->followRedirects(true);

    $session = $this->client->getContainer()->get('session');
    $this->client->getCookieJar()->set(new \Symfony\Component\BrowserKit\Cookie($session->getName(), $session->getId()));

    # request
    $this->client->request('GET', '/');

    $token = new UsernamePasswordToken('Admin', 'real_password', 'secured_area', array());
    $session->set('_security_secured_area', serialize($token));
    $session->save();   
}

}

Test command line

# specify the configuration directory on the command line

phpunit -c app

# run all tests in the Utility directory

$ phpunit -c app src/Acme/DemoBundle/Tests/Utility/

# run tests for the Calculator class

Don't forget the (s) for the Tests directory file

$ phpunit -c app src/Acme/DemoBundle/Tests/Utility/CalculatorTest.php

Current configuration in app/phpunit.xml file in:

<?xml version="1.0" encoding="UTF-8"?>
<phpunit
    backupGlobals               = "false"
    backupStaticAttributes      = "false"
    colors                      = "true"
    convertErrorsToExceptions   = "true"
    convertNoticesToExceptions  = "true"
    convertWarningsToExceptions = "true"
    processIsolation            = "false"
    stopOnFailure               = "false"
    syntaxCheck                 = "false"
    bootstrap                   = "bootstrap.php.cache" >


<testsuites>
    <testsuite name="Project Test Suite">
        <directory>../src/*/*Bundle/Tests</directory>
        <directory>../src/*/Bundle/*Bundle/Tests</directory>
    </testsuite>
</testsuites>


</phpunit>

# run all tests for the entire Bundle

$ phpunit -c app src/Acme/DemoBundle/

setUp() vs setUpBeforeClass()

setUpBeforeClass() method is executed only once per class, and even before object is constructed, and that is reason why it is marked as static public function. setUp() method is called before every test case, which mean that this method can be called few times per one test class.

Similar is difference between tearDownAfterClass and tearDown(), tearDown() method is called after each test case, and tearDownAfterClass() method is called after all tests in class finish, and after last tearDown() method is called.

Test code sample with data base connection and Service in Symfony 2

How to instantiate your custom service for test


class MyClassTest extends WebTestCase 
{ 

protected $MyService; 
protected $dataMyService; 
protected $em; 
protected 
static $kernel;

protected function setUp()
{
    parent::setUp();
    static::$kernel = static::createKernel();
    static::$kernel->boot();
    $container = static::$kernel->getContainer();
    $this->MyService = $container->get('vm.myservice');
    $this->em = $container->get('doctrine')->getManager(); // To be used

    // Data for fixtures
    $this->dataMyService = array('envoiId' => 0,
        'userId' => 6,                   
        'dateInsert' => date("Y-m-d H:i:s"),
        'dateRoutage' => date("Y-m-d H:i:s"),
        'object' => 'MyService 666',
        'status' => 0,                     
        'SenderID' => 'somebody'
        );
}

public static function setUpBeforeClass()
{
    static::$kernel = static::createKernel();
    static::$kernel->boot();
    $em = static::$kernel->getContainer()->get('doctrine')->getManager();

    // Empty MyService Data
    $myserviceDel = $em->getRepository('AcmeMyBundle:MyServiceDAO')->findBy(array('object' => 'MyService 666'));

    foreach ($myserviceDel as $rel) {
        $em->remove($rel);
    }
    $em->flush();

}

/**
 * @covers Acme\MyBundle\Model\MyService::load
 * Implements testLoad().
 */
public function testLoad()
{
    $myserviceData = $this->MyService->load(1);
    $this->assertEquals("1",$myserviceData["myserviceId"]);
}

/**
 * @covers Acme\MyBundle\Model\MyService::add
 * Implements testAdd().
 */
public function testAdd()
{
    // 1. Add data
    $this->idCreated = $this->MyService->add($this->dataMyService);
    // 2. Verify it exists
    $this->assertGreaterThan(0,$this->idCreated);
}

public static function tearDownAfterClass()
{
    static::$kernel = static::createKernel();
    static::$kernel->boot();
    $em = static::$kernel->getContainer()->get('doctrine')->getManager();

    // CLEAN UP DATA FOR MyService
    $myserviceDel = $em->getRepository('AcmeMyBundle:MyServiceDAO')->findBy(array('object' => 'MyService 666'));

    foreach ($myserviceDel as $rel) {
        $em->remove($rel);
    }

    $em->flush();
    unset($em);

    if (null !== static::$kernel) {
        static::$kernel->shutdown();
    }
}

public function tearDown()
{
    parent::tearDown();
    unset($this->em);
    unset($this->MyService);
}

}