unit test querybuilder kerneltestcase app php symfony doctrine2 phpunit

test - ¿Cómo configurar las pruebas de unidades pesadas de la base de datos en Symfony2 usando PHPUnit?



symfony test querybuilder (3)

Nunca he usado PHPUnit_Extensions_Database_TestCase , principalmente porque estas dos razones:

  • No escala bien Si configura y desmonta la base de datos para cada prueba y tiene una aplicación que depende en gran medida de la base de datos, termina creando y soltando el mismo esquema una y otra vez.
  • Me gusta tener mis accesorios no solo dentro de mis pruebas sino también dentro de mi base de datos de desarrollo, y algunos accesorios incluso son necesarios para la producción (usuario administrador inicial o categorías de productos o lo que sea). Tenerlos dentro de un xml que solo se puede usar para phpunit no me parece correcto.

Mi camino en teoría ...

Uso la doctrine/doctrine-fixtures-bundle para accesorios (sin importar el propósito) y configuro toda la base de datos con todos los accesorios. Luego ejecuto todas las pruebas en esta base de datos y me aseguro de volver a crear la base de datos si una prueba la cambió.

Las ventajas son que no necesito configurar una base de datos nuevamente si una prueba solo se lee pero no cambia nada. Para los cambios, tengo que soltarlo y volver a crearlo o asegurarme de revertir los cambios.

Utilizo sqlite para probar porque puedo configurar la base de datos, luego copiar el archivo sqlite y reemplazarlo por uno limpio para recuperar la base de datos original. De esa forma no tengo que soltar la base de datos, crearla y cargar todos los dispositivos de nuevo para una base de datos limpia.

... y en código

Escribí un artículo sobre cómo hago pruebas de base de datos con symfony2 y phpunit .

Aunque utiliza sqlite, creo que uno puede hacer fácilmente los cambios para usar MySQL o Postgres o lo que sea.

Pensando más

Aquí hay algunas otras ideas que podrían funcionar:

  • Una vez leí acerca de una configuración de prueba en la que antes de usar la base de datos se inicia una transacción (dentro del método setUp) y luego se utiliza el método tearDown para deshacer. De esta forma, no necesita configurar la base de datos nuevamente y solo necesita inicializarla una vez.
  • Mi configuración descrita anteriormente tiene el inconveniente de que la base de datos está configurada cada vez que se ejecuta phpunit, incluso si solo ejecuta algunas pruebas de unidad sin interacción con la base de datos. Estoy experimentando con una configuración en la que utilizo una variable global que indica si la base de datos se configuró y luego dentro de las pruebas llamamos a un método que verifica esta variable e inicializa la base de datos si aún no sucedió. De esa manera, solo cuando una prueba necesita la base de datos, la configuración se llevaría a cabo.
  • Un problema con sqlite es que no funciona igual que MySQL en algunos casos raros. Tuve un problema una vez que algo se comportaba de manera diferente en MySQL y sqlite, lo que causaba que fallara una prueba cuando funcionaba con MySQL. No puedo recordar qué era exactamente.

Soy bastante nuevo en el mundo de las pruebas y quiero asegurarme de estar en el camino correcto.

Estoy intentando configurar pruebas unitarias en un proyecto symfony2 usando phpunit .

PHPUnit funciona y las sencillas pruebas de controlador predeterminadas funcionan bien. (Sin embargo, esto no se trata de pruebas funcionales, sino de una prueba de mi aplicación).

Sin embargo, mi proyecto depende en gran medida de las interacciones de la base de datos, y por lo que entiendo de la documentación de phpunit , debo configurar una clase basada en /PHPUnit_Extensions_Database_TestCase , luego crear elementos para mi db y trabajar desde allí.

Sin embargo, symfony2 solo ofrece una clase WebTestCase que solo se extiende desde /PHPUnit_Framework_TestCase de /PHPUnit_Framework_TestCase .

Entonces, ¿tengo razón en suponer que debería crear mi propio DataBaseTestCase que en su mayoría copia WebTestCase , con la única diferencia de que se extiende desde /PHPUnit_Extensions_Database_TestCase e implementa todos sus métodos abstractos?

¿O hay otro flujo de trabajo recomendado "incorporado" para symfony2 en relación con las pruebas centradas en la base de datos?

Como quiero asegurarme de que mis modelos almacenen y recuperen los datos correctos, no quiero terminar probando los detalles de la doctrina por accidente.


Puedes usar esta clase:

<?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(); } }

Y luego puedes probar tu entidad. Algo como esto (suponiendo que tenga una entidad Usuario)

//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"); ... } }

Espero que esto ayude.

Fuente: theodo.fr/blog/2011/09/symfony2-unit-database-tests


tl; dr:

  • Si, y solo si quieres ir a la ruta de prueba funcional completa, te recomiendo buscar la respuesta de Sgoettschkes .
  • Si desea probar su aplicación de forma individual y debe probar el código que interactúa con la base de datos, lea o salte directamente a Symfony2 documentos
Hubo ciertos aspectos en mi pregunta original que dejan en claro que faltaba mi comprensión de las diferencias entre las pruebas unitarias y las pruebas funcionales. (Como ya he escrito, quiero probar la aplicación de manera unitaria, pero también estaba hablando de la prueba del controlador al mismo tiempo, y esas son pruebas funcionales por definición).

Las pruebas unitarias solo tienen sentido para los servicios y no para los repositorios. Y esos servicios pueden usar burlas del administrador de la entidad. (Incluso iría tan lejos como para decir: si es posible, escriba servicios que solo esperan que las entidades pasen a ellos. Entonces solo necesita crear burlas de esas entidades y las pruebas unitarias de su lógica de negocios se vuelven muy sencillas).

Mi caso de uso real para mi aplicación se reflejó bastante bien en los documentos de Symfony2 sobre cómo probar el código que interactúa con la base de datos .

Proporcionan este ejemplo para una prueba de servicio:

Clase de servicio:

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(); } }

Clase de prueba de servicio:

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 se requiere una base de datos de prueba para ese tipo de prueba, solo burla.

Como es importante probar la lógica comercial, no la capa de persistencia.

Solo para las pruebas funcionales tiene sentido tener su propia base de datos de prueba que uno debe construir y derribar luego, y la gran pregunta debería ser:

¿Cuándo tiene sentido la prueba funcional?

Solía ​​pensar que probar todas las cosas es la respuesta correcta; sin embargo, después de trabajar con un montón de software heredado que, en sí mismo, apenas fue desarrollado con pruebas, me he vuelto un poco más perezoso y considero que ciertas funcionalidades funcionan hasta que se demuestre lo contrario por un error.

Supongamos que tengo una aplicación que analiza un XML, crea un objeto y almacena esos objetos en una base de datos. Si se sabe que funciona la lógica que almacena los objetos en la base de datos (como en: la empresa requiere los datos y, hasta el momento, no está rota), e incluso si esa lógica es una gran pila de basura, no hay inminente necesidad de probar eso. Como todo lo que necesito para asegurarme de que mi analizador XML extraiga los datos correctos. De la experiencia puedo deducir que se almacenarán los datos correctos.

Hay escenarios en los que las pruebas funcionales son bastante importantes, es decir, si uno escribiera una tienda en línea. Allí sería crítico para los negocios que los artículos comprados se almacenaran en la base de datos y aquí las pruebas funcionales con toda la base de datos de prueba tienen sentido absoluto.