-->

Testing Controllers in Symfony2 with Doctrine

2019-01-22 04:38发布

问题:

I have created a very simple REST controller in Symony2 with Database insert/updates/deletes in the controller actions.

Is there a nice way to write unit/integration tests for these controller actions without polluting the production database? Do I have to work with different environments - or is there a proposed way from the framework vendor for this?

Current Controller Example:

public function postAction()
{
    $json = $this->getRequest()->getContent();
    $params = json_decode($json);
    $name = $params->name;
    $description = $params->description;

    $sandbox = new Sandbox();
    $sandbox->setName($name);
    $sandbox->setDescription($description);
    $em = $this->getDoctrine()->getManager();
    $em->persist($sandbox);
    $em->flush();

    $response = new Response('/sandbox/'.$sandbox->getId());
    $response->setStatusCode(201);
    return $response;
}

Current Test Example:

class SandboxControllerTest extends WebTestCase
{

    public function testRest()
    {
        $client = static::createClient();

        $crawler = $client->request('POST', '/service/sandbox', array(), array(), array(), json_encode(array('name' => 'TestMe', 'description' => 'TestDesc')));

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

回答1:

In my opinion you should definitely avoid change database with your tests.

My favourite way to achieve this is inject entity manager mock inside a test client. For example:

public function testRest()
{
    // create entity manager mock
    $entityManagerMock = $this->getMockBuilder('Doctrine\ORM\EntityManager')
        ->setMethods(array('persist', 'flush'))
        ->disableOriginalConstructor()
        ->getMock();

    // now you can get some assertions if you want, eg.:
    $entityManagerMock->expects($this->once())
        ->method('flush');

    // next you need inject your mocked em into client's service container
    $client = static::createClient();
    $client->getContainer()->set('doctrine.orm.default_entity_manager', $entityManagerMock);

    // then you just do testing as usual
    $crawler = $client->request('POST', '/service/sandbox', array(), array(), array(), json_encode(array('name' => 'TestMe', 'description' => 'TestDesc')));

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

One thing with this solution which you should be aware is that you need inject your mocked service before each request. This is because the client reboots a kernel between each request (which means that the container is rebuild as well).

edit:

My GET approach in controller's tests is that I can mock entity repositories and so on in order to stub every getting data from db but it's a lot of work and it's not very comfortable, so I prefer in this case (I mean only if we speak about controller's test) actually getting real data from db. By real data I mean data created with doctrine fixtures. And as long as we don't change database we can depend on the fixtures.

But if we are speaking about changing data inside db (POST/PUT/DELETE methods) I always use mocks. If you'll use em mock and set appropriate expectations on "perist" and "flush" methods, you can be sure that the data is correctly created/updated/deleted actually without any database's modifications.



回答2:

Here is what I do

in your test class add the $kernel static variable and create a function that loads the kernel in test mode

protected static $kernel;
protected static $container;

public static function setUpBeforeClass()
{
    self::$kernel = new \AppKernel('test', true);
    self::$kernel->boot();

    self::$container = self::$kernel->getContainer();
}

And as the first line of test function ,call self:setUpBeforeClass()

This makes symfony load the config_test.yml configuration file and you can define a different database connection there



回答3:

A good way to do this, especially if you need to have some test data in your database is using a separate SQLite Database together with the DoctrineFixturesBundle.

  • Here is how to install the FixturesBundle
  • A clean way to set up your Fixtures and Tests

    Make sure you have the SQLite extension activated in your php