Siembra de datos de fábrica
Keep on Learning!
If you liked what you've learned so far, dive in! Subscribe to get access to this tutorial plus video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeTengo que confesarte algo: ¡He estado haciéndonos trabajar demasiado!
Para sembrar la base de datos, instanciamos la entidad, cogemos el Gestor de Entidades y, a continuación, la persistimos y la vaciamos. Esto no tiene nada de malo, pero Foundry está a punto de hacernos la vida mucho más fácil.
Generar la Fábrica
En tu terminal, ejecuta:
php bin/console make:factory
Este comando viene de Foundry. Seleccionaré generar todas las fábricas.
La idea es que crees una fábrica para cada entidad para la que quieras crear datos ficticios, ya sea en una prueba o para tus fixtures normales. Sólo necesitamosLockDownFactory
, pero eso está bien.
Gira y mira src/Factory/LockDownFactory.php
. No voy a hablar demasiado de estas clases de fábrica: ya las tratamos en nuestro tutorial de Doctrine. Pero esta clase facilitará la creación de objetos LockDown
, incluso estableciendocreatedAt
en un DateTime
aleatorio, reason
en un texto aleatorio y status
en uno de los estados válidos, por defecto.
// ... lines 1 - 30 | |
final class LockDownFactory extends ModelFactory | |
{ | |
// ... lines 33 - 47 | |
protected function getDefaults(): array | |
{ | |
return [ | |
'createdAt' => \DateTimeImmutable::createFromMutable(self::faker()->dateTime()), | |
'reason' => self::faker()->text(), | |
'status' => self::faker()->randomElement(LockDownStatus::cases()), | |
]; | |
} | |
// ... lines 56 - 70 | |
} |
Utilizar la Fábrica en una prueba
Utilizar esto en una prueba es una delicia. Digamos LockDownFactory::createOne()
. Aquí, podemos pasar una matriz de cualquier campo que queramos establecer explícitamente. Lo único que nos importa es que este LockDown
tenga un estado ACTIVE
. Por tanto, establecestatus
en LockDownStatus::ACTIVE
.
// ... lines 1 - 6 | |
use App\Factory\LockDownFactory; | |
// ... lines 8 - 13 | |
class LockDownRepositoryTest extends KernelTestCase | |
{ | |
// ... lines 16 - 24 | |
public function testIsInLockDownReturnsTrueIfMostRecentLockDownIsActive() | |
{ | |
// ... lines 27 - 28 | |
LockDownFactory::createOne([ | |
'status' => LockDownStatus::ACTIVE, | |
]); | |
// ... lines 32 - 33 | |
} | |
// ... lines 35 - 39 | |
} |
Ya está No necesitamos crear este LockDown
y no necesitamos el Gestor de Entidades. Esa única llamada se encarga de todo.
Observa, cuando ejecutemos la prueba:
symfony php vendor/bin/phpunit tests/Integration/Repository/LockDownRepositoryTest.php
¡Pasa! Me encanta.
Objetos proxy Foundry
Por cierto, el método LockDownRepository
devuelve el nuevo objeto LockDown
... lo que a menudo puede ser útil. Pero en realidad está envuelto en un objeto proxy especial. Así que si ejecutamos la prueba ahora, puedes ver que es un proxy... y que el LockDown
se esconde dentro.
¿Por qué hace eso Foundry? Bueno, si vas a buscar su documentación, tienen toda una sección sobre el uso de esta biblioteca dentro de las pruebas. Un punto habla del proxy de objetos. El proxy te permite llamar a todos los métodos normales de tu entidad más varios métodos adicionales, como ->save()
, ->remove()
o incluso ->repository()
para obtener otro objeto proxy que envuelve al repositorio.
Así que parece y actúa como tu objeto normal, pero con algunos métodos adicionales. Eso no es importante para nosotros ahora, sólo quería que lo tuvieras en cuenta. Si necesitas el objeto entidad real, puedes llamar a ->object()
para obtenerlo.
// ... lines 1 - 24 | |
public function testIsInLockDownReturnsTrueIfMostRecentLockDownIsActive() | |
{ | |
// ... lines 27 - 31 | |
dd($lockDown->assertNotPersisted()); | |
// ... lines 33 - 34 | |
} | |
// ... lines 36 - 42 |
Añadir más objetos
De todos modos, ahora que añadir datos es tan sencillo, podemos hacer rápidamente que nuestra prueba sea más robusta. Para ver si podemos engañar a mi consulta, llama a createMany()
... para crear 5 objetos LockDown
con LockDownStatus::ENDED
.
Para asegurarnos de que nuestra consulta sólo mira el LockDown
más reciente , para el activo, establece su createdAt
en -1 day
. Y para los ENDED
, establécelos en algo más antiguo.
// ... lines 1 - 24 | |
public function testIsInLockDownReturnsTrueIfMostRecentLockDownIsActive() | |
{ | |
// ... lines 27 - 28 | |
LockDownFactory::createOne([ | |
'createdAt' => new \DateTimeImmutable('-1 day'), | |
'status' => LockDownStatus::ACTIVE, | |
]); | |
LockDownFactory::createMany(5, [ | |
'createdAt' => new \DateTimeImmutable('-2 day'), | |
'status' => LockDownStatus::ENDED, | |
]); | |
// ... lines 37 - 38 | |
} | |
// ... lines 40 - 46 |
Veamos si nuestra consulta es lo suficientemente robusta como para seguir comportándose correctamente.
symfony php vendor/bin/phpunit tests/Integration/Repository/LockDownRepositoryTest.php
¡Lo es!
Pero... en realidad... la gestión tiene algunas reglas extra complicadas en torno a un bloqueo. Copia esta prueba, pégala y cámbiale el nombre atestIsInLockdownReturnsFalseIfTheMostRecentIsNotActive
.
// ... lines 1 - 40 | |
public function testIsInLockDownReturnsFalseIfMostRecentIsNotActive() | |
{ | |
// ... lines 43 - 52 | |
} | |
// ... lines 54 - 60 |
Para explicar la extraña regla de la dirección, permíteme modificar los datos. Haz que el primer LockDown
seaENDED
... luego el siguiente, más antiguo de 5 estados ACTIVE
. Por último, assertFalse()
al final.
// ... lines 1 - 40 | |
public function testIsInLockDownReturnsFalseIfMostRecentIsNotActive() | |
{ | |
self::bootKernel(); | |
LockDownFactory::createOne([ | |
'createdAt' => new \DateTimeImmutable('-1 day'), | |
'status' => LockDownStatus::ENDED, | |
]); | |
LockDownFactory::createMany(5, [ | |
'createdAt' => new \DateTimeImmutable('-2 days'), | |
'status' => LockDownStatus::ACTIVE, | |
]); | |
$this->assertFalse($this->getLockDownRepository()->isInLockDown()); | |
} | |
// ... lines 56 - 62 |
Eso... puede parecer confuso... y en cierto modo lo es. Según la dirección, al determinar si estamos en bloqueo, SÓLO debemos mirar el estado LockDown
MÁS reciente. Si hay bloqueos activos más antiguos... esos, aparentemente, no importan.
No es sorprendente que, cuando probamos las pruebas:
symfony php vendor/bin/phpunit tests/Integration/Repository/LockDownRepositoryTest.php
Ésta falla. Pero, mira el lado bueno: ¡esa prueba fue superrápida de escribir! Y ahora podemos entrar en LockDownRepository
para arreglar las cosas. Avanzaré por algunos cambios que recuperan el LockDown
más reciente, independientemente de su estado.
Si no encontramos ningún bloqueo, devolveré false. Si no, añadiré un assert()
para ayudar a mi editor... y devolveré true si el estado no es igual aLockDownStatus::ENDED
.
// ... lines 1 - 17 | |
class LockDownRepository extends ServiceEntityRepository | |
{ | |
// ... lines 20 - 24 | |
public function isInLockDown(): bool | |
{ | |
// find the most recent lock down | |
$lockDown = $this->createQueryBuilder('lock_down') | |
->orderBy('lock_down.createdAt', 'DESC') | |
->setMaxResults(1) | |
->getQuery() | |
->getOneOrNullResult(); | |
if (!$lockDown) { | |
return false; | |
} | |
assert($lockDown instanceof LockDown); | |
return $lockDown->getStatus() !== LockDownStatus::ENDED; | |
} | |
} |
Y ahora
symfony php vendor/bin/phpunit tests/Integration/Repository/LockDownRepositoryTest.php
¡Estamos en verde!
Utilizar la función LockDown
Llevamos tanto tiempo viviendo en nuestro terminal que creo que deberíamos celebrarlo utilizándolo en nuestro sitio. En los accesorios, he añadido un LockDown
activo por defecto.
Dirígete a MainController
... y autoconecta LockdownRepository $lockdownRepository
. A continuación, lanza una nueva variable en la plantilla llamada isLockedDown
ajustada a$lockdownRepository->isInLockdown()
.
// ... lines 1 - 6 | |
use App\Repository\LockDownRepository; | |
// ... lines 8 - 13 | |
class MainController extends AbstractController | |
{ | |
// ... line 16 | |
public function index(GithubService $github, DinosaurRepository $repository, LockDownRepository $lockDownRepository): Response | |
{ | |
// ... lines 19 - 24 | |
return $this->render('main/index.html.twig', [ | |
// ... line 26 | |
'isLockedDown' => $lockDownRepository->isInLockDown(), | |
]); | |
} | |
// ... lines 30 - 39 | |
} |
Por último, en la plantilla - templates/main/index.html.twig
- ya tengo una plantilla_lockdownAlert.html.twig
. Si, isLockedDown
, incluye eso.
// ... lines 1 - 2 | |
{% block body %} | |
{% if isLockedDown %} | |
{{ include('main/_lockDownAlert.html.twig') }} | |
{% endif %} | |
// ... lines 7 - 54 | |
{% endblock %} |
Momento de la verdad. Refresca. Ejecuta: ¡Sálvese quien pueda! ¡Estamos en bloqueo!
Siguiente: necesitamos una forma de desactivar el bloqueo. Porque, si hago clic en esto,... ¡no hace nada! Para ayudarnos con esta nueva tarea, vamos a utilizar una prueba de integración en una clase diferente: en uno de nuestros servicios normales.