Saddam Hawari
Saddam Hawari

Reputation: 79

Functional testing in Symfony 5

This is my symfony project where I am practising functional test and I have such error when I am testing my function.

enter image description here

Here, The section of code in which my error occur:\

<?php

namespace App\Tests;

use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use App\Entity\Category;

class AdminControllerCategoriesTest extends WebTestCase
{
public function setUp():void
{
    parent::setUp();
    $this->client = static::createClient();

    $this->entityManager = $this->client->getContainer()->get('doctrine.orm.entity_manager');

    $this->entityManager->beginTransaction();

    $this->entityManager->getConnection()->setAutoCommit(false);
}

public function tearDown():void
{
    parent::tearDown();
    $this->entityManager->rollback();
    $this->entityManager->close();
    $this->entityManager = null; //avoid memory leaks
}

public function testTextOnPage()
{
    $crawler = $this->client->request('GET', '/admin/categories');
    $this->assertSame('Categories list', $crawler->filter('h2')->text());
    $this->assertContains('Electronics', $this->client->getResponse()->getContent());
}

public function testNumberOfItems()
{
    $crawler = $this->client->request('GET', '/admin/categories');
    $this->assertCount(21, $crawler->filter('option'));
}
}

Here, my .env, Where I have my database connection:

    # In all environments, the following files are loaded if they exist,
# the latter taking precedence over the former:
#
#  * .env                contains default values for the environment variables needed by the app
#  * .env.local          uncommitted file with local overrides
#  * .env.$APP_ENV       committed environment-specific defaults
#  * .env.$APP_ENV.local uncommitted environment-specific overrides
#
# Real environment variables win over .env files.
#
# DO NOT DEFINE PRODUCTION SECRETS IN THIS FILE NOR IN ANY OTHER COMMITTED FILES.
#
# Run "composer dump-env prod" to compile .env files for production use (requires symfony/flex >=1.2).
# https://symfony.com/doc/current/best_practices.html#use-environment-variables-for-infrastructure-configuration

###> symfony/framework-bundle ###
APP_ENV=dev
APP_SECRET=018d7408d23791c60854cbb4fc65b667
###< symfony/framework-bundle ###

###> doctrine/doctrine-bundle ###
# Format described at https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url
# IMPORTANT: You MUST configure your server version, either here or in config/packages/doctrine.yaml
#
# DATABASE_URL="sqlite:///%kernel.project_dir%/var/data.db"
DATABASE_URL="mysql://root:@127.0.0.1:3306/symf5?serverVersion=mariadb-10.4.11"
# DATABASE_URL="postgresql://symfony:[email protected]:5432/app?serverVersion=13&charset=utf8"
###< doctrine/doctrine-bundle ###

Here, I have following code in my .env.test file:

    # define your env variables for the test env here
KERNEL_CLASS='App\Kernel'
APP_SECRET='$ecretf0rt3st'
SYMFONY_DEPRECATIONS_HELPER=999999
PANTHER_APP_ENV=panther
PANTHER_ERROR_SCREENSHOT_DIR=./var/error-screenshots

I don't know what is problem with, I have tried different methods but it doesn't work and I also don't know what wrong with this and what to do.Hope you guys help me sort out my problem. Thank you!

Upvotes: 5

Views: 2182

Answers (3)

Hassan Elshazly Eida
Hassan Elshazly Eida

Reputation: 839

you have two options:

  1. Create new database for your testing
  2. remove dbname_suffix in your db test which is responsible for giving suffix name for new database test -at config/packages/test/doctrine.yaml -
when@test:
    doctrine:
        dbal:
            # "TEST_TOKEN" is typically set by ParaTest
            dbname_suffix: '_test%env(default::TEST_TOKEN)%'

Upvotes: 10

WPler
WPler

Reputation: 21

How looks like your phpunit.xml or does you have one?

We have add a phpunit.xml in the project directory and declare our necessary env variables in the phpunit.xml file, like:

<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
    colors="true"
    bootstrap="vendor/autoload.php"
    cacheResultFile=".phpunit.cache/test-results"
    executionOrder="depends,defects"
    forceCoversAnnotation="true"
    beStrictAboutCoversAnnotation="true"
    beStrictAboutOutputDuringTests="true"
    beStrictAboutTodoAnnotatedTests="true"
    convertDeprecationsToExceptions="true"
    failOnRisky="true"
    failOnWarning="true"
    verbose="true"
>
    <php>
        <ini name="display_errors" value="1" />
        <ini name="error_reporting" value="1" />
        <env name="APP_ENV" value="test" force="true" />
        <env name="KERNEL_CLASS" value="App\Kernel" />
        <env name="APP_DEBUG" value="false" />
        <env name="DATABASE_URL" value="sqlite:///:memory:" force="true" />
        <var name="DB_DBNAME" value="app" />
    </php>

    <testsuites>
        <testsuite name="Test Suite">
            <directory>tests</directory>
        </testsuite>
    </testsuites>

    <coverage cacheDirectory=".phpunit.cache/code-coverage" processUncoveredFiles="true">
        <include>
            <directory suffix=".php">src</directory>
        </include>
        <exclude>
            <directory>src/Entity</directory>
            <directory>src/Repository</directory>
            <file>src/Kernel.php</file>
        </exclude>
    </coverage>
    
    <listeners>
        <listener class="Symfony\Bridge\Phpunit\SymfonyTestsListener" />
    </listeners>

    <extensions>
        <extension class="Symfony\Component\Panther\ServerExtension" />
    </extensions>
</phpunit>

For setup all functional tests we init the database schema on a tests/Framework/FunctionalTestCase.php

<?php 

namespace App\Tests\Framework;

use App\Tests\Framework\DatabaseUtil\InitDatabase;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\KernelBrowser;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;

class FunctionalTestCase extends WebTestCase
{
    protected EntityManagerInterface|null $entityManager = null;
    private KernelBrowser|null $client = null;

    protected function setUp(): void 
    {
        parent::setUp();
        
        self::ensureKernelShutdown();
        $this->client = static::createClient();

        InitDatabase::updateSchema($this->client);
        $this->entityManager = $this->client->getContainer()
            ->get('doctrine')
            ->getManager();
    }

    protected function getClientFromParent(): KernelBrowser
    {
        return $this->client;
    }
}

And the tests/Framework/DatabaseUtil/InitDataBase.php:

<?php

namespace App\Tests\Framework\DatabaseUtil;

use Doctrine\ORM\Tools\SchemaTool;

class InitDatabase
{
    public static function updateSchema(object $kernel): void
    {
        $entityManager = $kernel->getContainer()->get('doctrine.orm.entity_manager');
        $metaData = $entityManager->getMetadataFactory()->getAllMetadata();
        $schemaTool = new SchemaTool($entityManager);
        $schemaTool->updateSchema($metaData);
    }
}

Using

We using this FunctionalTestCase in our ControllerTests like:

<?php

namespace App\Tests\Controller\AnyController;

use App\Tests\Framework\FunctionalTestCase;
use App\Entity\User;
use App\TestsDataFixture\UserFixture;
use Doctrine\Common\Collections\Collection;
use Doctrine\DBAL\Exception\TableNotFoundException;
use Doctrine\Persistence\ObjectManager;

class AnyControllerTest extends FunctionalTestCase
{
    private User $user;
    private User $entityUser;
    private KernelBrowser $client;
    
    public function setUp(): void
    {
        parent::setUp();
        
        $userFixture = new UserFixture();
        $this->user = $userFixture->load($this->entityManager);
        $this->entityUser = $this->entityManager->getRepository(User::class)->findAll()[0];

        $this->client = $this->getClientFromParent();
    }

    public function tearDown(): void
    {
        parent::tearDown();
        $this->delete([$this->entityUser], $this->entityManager);
    }

    public function testLoginSuccessful(): void 
    {
        $payload = [
            'username' => $this->user->getEmail(),
            'password' => $this->user->getPassword()
        ];
        
        $this->client->loginUser($this->user);

        $this->client->request(
            'POST',
            '/auth/login',
            [],
            [],
            [
                'Content-Type' => 'application/json'
            ],
            json_encode($payload)
        );

        $response = $this->client->getResponse()->getContent();
        $data = json_decode($response, true);
        
        $this->assertResponseIsSuccessful();
        $this->assertIsString($data['token']);
    }

    private function deleteFromDatabase(array|Collection $entities, ObjectManager $manager): void 
    {
        $connection = $manager->getConnection();
        $databasePlatform = $connection->getDatabasePlatform();

        if ($databasePlatform->supportsForeignKeyConstraints()) {
            $connection->query('SET FOREIGN_KEY_CHECKS=0');
        }

        foreach($entities as $entity) {
            try {
                $query = $databasePlatform->getTruncateTableSQL(
                    $manager->getClassMetadata(get_class($entity))->getTableName()
                );
                $connection->executeUpdate($query);
            } catch(TableNotFoundException $exception) {
                // do nothing
            }
        }
        
        if ($databasePlatform->supportsForeignKeyConstraints()) {
            $connection->query('SET FOREIGN_KEY_CHECKS=1');
        }
    }
}

The UserFixture is a normal DataFixture with a load method to generate a FakeUser like in this example: https://symfony.com/bundles/DoctrineFixturesBundle/current/index.html

You can put the private delete method in a trait for using in more than one controller.

In this example we use the in-memory sqlite database, but you can change the DATABASE_URL in phpunit also to a MariaDB DSN.

Upvotes: 0

A.DREY
A.DREY

Reputation: 38

I think you might forget to create the .env.test file.

In the docs, you can read that need this file :

https://symfony.com/doc/current/testing.html#customizing-environment-variables

In this file, you will use the right database for your tests.

Tell me if it's working !

Upvotes: 0

Related Questions