Symfony单元测试中的自定义存储库类

3

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

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

我怀疑问题可能出在我调用存储库的方式上,但不确定如何解决它。

tests / 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类的路径在用户实体类中使用以下注释指定:

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

有什么办法可以让测试使用自定义的存储库类?我认为我可能需要在 $userRepository = $this->createMock(ObjectRepository::class); 中做出一些改变,例如使用 $this->getMockBuilder() ?

你期望的是注册表方法('find'),而不是findLastRegisteredUsers。 - Cerad
1
哎呀,我漏掉了那个 :) 但现在我遇到了一个错误:尝试配置方法“findLastRegisteredUsers”,但无法配置,因为它不存在,未指定,是final或static。 - Martin
你需要模拟UserRepository而不是基本的ObjectRepository。我可能在这方面缺少了什么。虽然有点离题,但你应该将UserRepository注入到你的UserCalculator中。 - Cerad
1个回答

3
你可以使用集成测试来完成此任务:
这个类扩展了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完全隔离地进行测试,并且出现错误:尝试配置无法配置的方法......

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

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

您需要将method('find')更改为method('findLastRegisteredUsers')

使用单元测试进行更新

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();
        } 
}

我已经将其作为集成测试运行,代码与您建议的非常相似。然而,我正在寻找一种解决方案,以便对单个类(单元测试)而不是通过不同应用程序层(功能测试)运行测试。但是非常感谢您的帮助,真的很感激。 - Martin
也许我错了,但在Symfony的示例中,他们使用了方法“find”,请尝试将其更改为->method('findLastRegisteredUsers') - nicandr
1
@Martin 是的,这会有帮助,this->method('findLastRegisteredUsers') 将会返回你用 new User() 创建并设置了数据的实例。 - nicandr
谢谢,@nic。我还在尝试使用单元测试。我已经按照你建议的进行了更改(更改了模拟中使用的存储库并将方法更改为“findLastRegisteredUsers”)。但现在我得到了“错误:调用未定义的方法Mock_ObjectRepository_e3bd2f4b :: findLastRegisteredUsers()”。 我还尝试在模拟中使用User Entity,但这给了我:“尝试配置无法配置的方法“findLastRegisteredUsers”,因为它不存在,未指定,是最终的或静态的”。我感到困惑。 - Martin
1
谢谢@nic,我做了一些傻事:忘记将存储库注入到objectManager中。为了保持简单,我没有粘贴我的整个代码。现在我正在将两个存储库注入到objectManager中->willReturn($userRepository,$otherRepository)。它工作得很好,感谢您的帮助。 - Martin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接