This course is still being released! Check back later for more chapters.

Get Notified About this Course!

We will send you messages regarding this course only
and nothing else, we promise.
You can unsubscribe anytime by emailing us at:
privacy@symfonycasts.com
Login to bookmark this video
Buy Access to Course
30.

Doctrine Tests

|

Share this awesome video!

|

Keep on Learning!

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

Time to change this integration test stub into a proper test for our bundle. We're going to use a real database and real entities for this!

Over in the terminal, install Zenstruck Foundry to help us manage our test database and fixtures:

symfony composer require --dev zenstruck/foundry

Test Entities

Back in our IDE, in the tutorial directory, copy the Entity folder into our bundle's tests/Fixture directory. If you don't see these entities, copy them from the script below:

// ... lines 1 - 2
namespace SymfonyCasts\ObjectTranslationBundle\Tests\Fixture\Entity;
use Doctrine\ORM\Mapping as ORM;
use SymfonyCasts\ObjectTranslationBundle\Mapping\Translatable;
use SymfonyCasts\ObjectTranslationBundle\Mapping\TranslatableProperty;
#[Translatable('entity1')]
#[ORM\Entity]
class Entity1
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
public int $id;
#[ORM\Column]
#[TranslatableProperty]
public string $property1;
}

// ... lines 1 - 2
namespace SymfonyCasts\ObjectTranslationBundle\Tests\Fixture\Entity;
use Doctrine\ORM\Mapping as ORM;
use SymfonyCasts\ObjectTranslationBundle\Model\Translation as BaseTranslation;
#[ORM\Entity]
class Translation extends BaseTranslation
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
public int $id;
}

Entity1 is our mock entity that we'll be translating in our tests. It's translatable, has an ID, and a translatable property.

The Translation entity is just like the one in our app.

More TestKernel Configuration

Over in our TestKernel, we need to tell it what bundles to load. Override the registerBundles() method. Inside, yield new FrameworkBundle(), yield new DoctrineBundle(), yield new ZenstruckFoundryBundle(), and finally, our bundle, yield new ObjectTranslationBundle():

// ... lines 1 - 15
class TestKernel extends Kernel
{
// ... lines 18 - 19
public function registerBundles(): iterable
{
yield new FrameworkBundle();
yield new DoctrineBundle();
yield new ZenstruckFoundryBundle();
yield new ObjectTranslationBundle();
}
// ... lines 27 - 51
}

Now we need more configuration. In configureContainer(), add $builder->loadFromExtension('symfonycasts_object_translation', ['translation_class' => Translation::class]). It's hard to see on this small screen, but we need to import the one from our test fixtures. I think this one is it. I'll scroll up to the namespaces to confirm. Yep, that's the one:

// ... lines 1 - 15
class TestKernel extends Kernel
{
// ... lines 18 - 27
private function configureContainer(ContainerConfigurator $container, LoaderInterface $loader, ContainerBuilder $builder): void
{
// ... lines 30 - 33
$container->extension('symfonycasts_object_translation', [
'translation_class' => Translation::class,
]);
// ... lines 37 - 50
}
}

Now to configure Doctrine. Add $builder->loadFromExtension('doctrine', []). First configure dbal with 'url' => 'sqlite:///%kernel.project_dir%/var/data.db':

// ... lines 1 - 15
class TestKernel extends Kernel
{
// ... lines 18 - 27
private function configureContainer(ContainerConfigurator $container, LoaderInterface $loader, ContainerBuilder $builder): void
{
// ... lines 30 - 37
$container->extension('doctrine', [
'dbal' => [
'url' => 'sqlite:///%kernel.project_dir%/var/data.db',
],
// ... lines 42 - 49
]);
}
}

For the orm configuration, I'm going to paste this snippet (you can grab it from the script below):

// ... lines 1 - 15
class TestKernel extends Kernel
{
// ... lines 18 - 27
private function configureContainer(ContainerConfigurator $container, LoaderInterface $loader, ContainerBuilder $builder): void
{
// ... lines 30 - 37
$container->extension('doctrine', [
// ... lines 39 - 41
'orm' => [
'mappings' => [
'Test' => [
'dir' => '%kernel.project_dir%/tests/Fixture/Entity',
'prefix' => 'SymfonyCasts\ObjectTranslationBundle\Tests\Fixture\Entity',
]
]
],
]);
}
}

This tells the Doctrine ORM where to find our test entities.

The ObjectTransator::translate() Test

Back in our test class, clear the existing test method. First, we need to create an instance of our mock entity, Entity1. We'll use a Foundry factory for this. We could create a real factory class, but to keep things simple, we'll use a dynamic factory. Write $entity = persist(), import the function from Foundry. Entity1::class as the first argument, and an array with 'property1' => 'value1' as the second:

// ... lines 1 - 12
class ObjectTranslatorTest extends KernelTestCase
// ... lines 14 - 16
public function testCanAccessService()
{
$entity = persist(Entity1::class, [
'property1' => 'value1',
]);
// ... lines 22 - 33
}
}

Now for the Translation entity: persist(Translation::class). Again, ensure you import the one from our test fixtures. The array will be 'objectType' => '', pop over to our Entity1 class to confirm the alias: entity1. Then 'objectId' => $entity->id, 'locale' => 'fr', 'field' => 'property1', and finally, 'value' => 'translated1':

// ... lines 1 - 12
class ObjectTranslatorTest extends KernelTestCase
// ... lines 14 - 16
public function testCanAccessService()
{
// ... lines 19 - 21
persist(Translation::class, [
'objectType' => 'entity1',
'objectId' => $entity->id,
'locale' => 'fr',
'field' => 'property1',
'value' => 'translated1',
]);
// ... lines 29 - 33
}
}

Down below, get our object translator service with $translator = self::getContainer()->get(ObjectTranslator::class):

// ... lines 1 - 12
class ObjectTranslatorTest extends KernelTestCase
// ... lines 14 - 16
public function testCanAccessService()
{
// ... lines 19 - 29
$translator = self::getContainer()->get(ObjectTranslator::class);
// ... lines 31 - 33
}
}

Translate the entity: $translated = $translator->translate($entity) and pass fr as the second argument to force translating to French:

// ... lines 1 - 12
class ObjectTranslatorTest extends KernelTestCase
// ... lines 14 - 16
public function testCanAccessService()
{
// ... lines 19 - 30
$translated = $translator->translate($entity, 'fr');
// ... lines 32 - 33
}
}

Finally, assert that the translated property is what we expect: $this->assertSame('translated1', $translated->property1);:

// ... lines 1 - 12
class ObjectTranslatorTest extends KernelTestCase
// ... lines 14 - 16
public function testCanAccessService()
{
// ... lines 19 - 32
$this->assertSame('translated1', $translated->property1);
}
}

Moment of truth! Back in the terminal, run our tests:

symfony php vendor/bin/phpunit

Darn! An error - "Foundry is not yet booted."

Ohhh, I forgot the required Foundry traits. Back in the test class, use Factories, which initializes Foundry, and ResetDatabase, which resets the database before each test:

// ... lines 1 - 12
class ObjectTranslatorTest extends KernelTestCase
{
use Factories, ResetDatabase;
// ... lines 16 - 34
}

Moment of truth, take two:

symfony php vendor/bin/phpunit

Sweet! All green! Our integration test with a real database is working!

Next, we'll test our bundle against different Symfony versions to ensure compatibility.