【问题标题】:Symfony Custom Repository class in Unit Tests单元测试中的 Symfony 自定义存储库类
【发布时间】:2018-06-16 13:32:04
【问题描述】:

我正在尝试创建一个单元测试来测试与数据库交互的 Symfony 4 代码。

需要测试的方法包含对自定义存储库类的调用,这会在运行 phpunit 时导致错误:错误:调用未定义的方法 Mock_ObjectRepository_583b1688::findLastRegisteredUsers()

我怀疑问题可能与我调用存储库的方式有关,但不确定如何解决。

测试/UserTest.php

class UserTest extends TestCase
{
    public function testCalculateTotalUsersReturnsInteger()
    {

        $user = new User();
        $user->setFullname('Test');

        // ! This might be what is causing the problem !
        $userRepository = $this->createMock(ObjectRepository::class); 

        $userRepository->expects($this->any())
            ->method('find')
            ->willReturn($user);

        $objectManager = $this->createMock(ObjectManager::class);

        $objectManager->expects($this->any())
            ->method('getRepository')
            ->willReturn($userRepository);

        $userCalculator = new RegistrationHandler($objectManager);
        $result = $registrationHandler->getAccountManager();

        $this->assertInternalType('int', $result);
    }
}

Repository/UserRepository.php

class UserRepository extends EntityRepository
{

    public function findLastRegisteredUsers($maxResults)
    {
        return $this->createQueryBuilder('user')
            ->andWhere('user.customField IS NOT NULL')
            ->addOrderBy('user.id', 'DESC')
            ->setFirstResult(0)
            ->setMaxResults($maxResults)
            ->getQuery()
            ->execute();
    }
}

src/User/UserCalculator.php

// src/User/UserCalculator.php
namespace App\User;

use Doctrine\Common\Persistence\ObjectManager;

class UserCalculator
{
    private $objectManager;

    public function __construct(ObjectManager $objectManager)
    {
        $this->objectManager = $objectManager;
    }

    public function calculateTotalUsers()
    {
        $userRepository = $this->objectManager
            ->getRepository(User::class);

        $users = $userRepository->findLastRegisteredUsers(100);

        // custom code
    }
}

为了清楚起见,自定义 EntityRepository 类的路径是在 User Entity 类中使​​用以下注释指定的

/**
 * @ORM\Entity(repositoryClass="App\Repository\UserRepository")
 */

任何想法如何让测试使用自定义存储库类? 我想我可能需要更改 $userRepository = $this->createMock(ObjectRepository::class); 例如使用 $this->getMockBuilder() ?

【问题讨论】:

  • 你期待注册方法('find'),而不是 findLastRegisteredUsers
  • 哎呀,错过了 :) 但现在我收到一个错误:尝试配置无法配置的方法“findLastRegisteredUsers”,因为它不存在,尚未指定,是最终的,或者是静态的
  • 您需要模拟 UserRepository 而不是基础 ObjectRepository。我一定在这里遗漏了一些东西。虽然有点跑题了,但您可能应该将 UserRepository 注入到您的 UserCalculator 中。

标签: unit-testing symfony doctrine phpunit


【解决方案1】:

您可以为此目的使用集成测试:

这个类扩展了KernelTestCase

class UserTest extends KernelTestCase
{
    public function testCalculateTotalUsersReturnsInteger()
    {

        self::bootKernel();
        $userCalculator = self::$kernel->getContainer()
        ->get('test.'.UserCalculator::class);


        $result = $userCalculator->calculateTotalUsers();

        $this->assertInternalType('int', $result);
    }
}

services_test.yml中你需要注册这个服务:

test.App\User\UserCalculator: '@App\User\UserCalculator'

你可以调用你的方法:

 $result = $userCalculator->calculateTotalUsers();
 $this->assertInternalType('int', $result);

并使用 Assert 进行测试。

关于集成测试的文档,我从那里得到了这个例子: https://knpuniversity.com/screencast/phpunit/integration-tests

更新: 如果您想使用 UniTest 进行完全隔离的测试并且遇到错误:正在尝试配置无法配置的方法...

您需要在第一个 mock 中直接模拟您的存储库

$userReppsitory = $this->createMock(UserRepository::class)

并且你需要将方法('find')更改为method('findLastRegisteredUsers')

使用 UnitTest 更新

use PHPUnit\Framework\TestCase;
use Doctrine\Common\Persistence\ObjectManager;
use App\User\UserCalculator;
class UserTest extends TestCase
{
     public function testCalculateTotalUsersReturnsInteger()
        {

            $employee = new User();
            $employee->setFullname('test');

            // Now, mock the repository so it returns the mock of the employee
            $employeeRepository = $this->createMock(\App\Repository\UserRepository::class);

            $employeeRepository->expects($this->any())
                ->method('findLastRegisteredUsers')
                ->willReturn($employee);
            // Last, mock the EntityManager to return the mock of the repository
            $objectManager = $this->createMock(ObjectManager::class);

            $objectManager->expects($this->any())
                ->method('getRepository')
                ->willReturn($employeeRepository);

            $userCalculator = new UserCalculator($objectManager);

            $result = $userCalculator->calculateTotalUsers();
        } 
}

【讨论】:

  • 我已经将它作为集成测试运行,代码与您的建议非常相似。但是,我正在寻找一种解决方案来针对单个类(单元测试)而不是通过不同应用程序的层(功能测试)运行测试。不过非常感谢您的帮助,非常感谢。
  • 也许我错了,但在 symfony 的示例中他们使用的是方法 'find',尝试将此 find 更改为 ->method('findLastRegisteredUsers')
  • @Martin 是的,这应该会有所帮助,this->method('findLastRegisteredUsers') 将返回您使用 new User() 创建的实例以及您设置的数据。
  • 谢谢,@nic。我仍在尝试使用单元测试。我已经进行了您建议的更改(更改模拟中使用的存储库并将方法更改为“findLastRegisteredUsers”)。但现在我收到“错误:调用未定义的方法 Mock_ObjectRepository_e3bd2f4b::findLastRegisteredUsers()”。我还尝试在模拟中使用用户实体,这给了我:“尝试配置无法配置的方法“findLastRegisteredUsers”,因为它不存在、尚未指定、是最终的或静态的”。我很困惑
  • 感谢@nic,我在做一些愚蠢的事情:忘记将存储库注入到 objectManager 中。为了简单起见,我没有粘贴我的整个代码。我现在在 objectManager ->willReturn( $userRepository, $otherRepository); 中注入两个存储库效果很好,感谢您的帮助
猜你喜欢
  • 2013-02-20
  • 1970-01-01
  • 2021-06-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-07
  • 1970-01-01
相关资源
最近更新 更多