Symfony 5 中的功能测试

问题描述 投票:0回答:3

这是我的 symfony 项目,我正在其中练习功能测试,当我测试我的功能时出现这样的错误。

enter image description here

这里,发生我的错误的代码部分:\

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

这里是我的.env,我有数据库连接:

    # 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 ###

在这里,我的 .env.test 文件中有以下代码:

    # 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

我不知道出了什么问题,我尝试了不同的方法,但都不起作用,我也不知道这出了什么问题,该怎么办。希望你们能帮我解决我的问题。 谢谢!

php symfony testing doctrine-orm functional-testing
3个回答
10
投票

您有两个选择:

  1. 为您的测试创建新数据库
  2. 删除数据库测试中的dbname_suffix,它负责为新数据库测试提供后缀名称 -at config/packages/test/doctrine.yaml -
when@test:
    doctrine:
        dbal:
            # "TEST_TOKEN" is typically set by ParaTest
            dbname_suffix: '_test%env(default::TEST_TOKEN)%'

0
投票

我想你可能忘记创建 .env.test 文件。

在文档中,您可以阅读需要此文件:

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

在此文件中,您将使用正确的数据库进行测试。

告诉我它是否有效!


0
投票

你的 phpunit.xml 看起来怎么样,或者你有吗?

我们在项目目录中添加了一个 phpunit.xml 并在 phpunit.xml 文件中声明了必要的环境变量,例如:

<?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>

为了设置所有功能测试,我们在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;
    }
}

以及测试/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);
    }
}

使用

我们在 ControllerTests 中使用这个FunctionalTestCase,例如:

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

UserFixture 是一个普通的 DataFixture,具有用于生成 FakeUser 的加载方法,如下例所示:https://symfony.com/bundles/DoctrineFixturesBundle/current/index.html

您可以将私有删除方法放入特征中,以便在多个控制器中使用。

在此示例中,我们使用内存中的 sqlite 数据库,但您也可以将 phpunit 中的 DATABASE_URL 更改为 MariaDB DSN。

© www.soinside.com 2019 - 2024. All rights reserved.