【问题标题】:symfony + phpunit - how to test UserFactory with PasswordEncoder dependenciessymfony + phpunit - 如何使用 PasswordEncoder 依赖项测试 UserFactory
【发布时间】:2020-09-06 17:12:58
【问题描述】:

您好,我的测试有问题。 我正在尝试测试通过 UserDto 数据创建 UserObject 的 UserFactory。 我不知道如何测试它,因为工厂在依赖项中需要 PasswordEncoder。

use App\Entity\User;
use App\Service\Factory\UserFactory;
use PHPUnit\Framework\MockObject\MockObject as MockObject;
use PHPUnit\Framework\TestCase;

class UserFactoryTest extends TestCase
{
    /**
     * @covers UserFactory
     */
    public function testShouldCreateUserObjectFromUserDto()
    {
        //Given
        /**
         * @var UserDto | MockObject
         */
        $userDto = $this->getMockBuilder(UserDto::class);

        //When
        $userFactory = new UserFactory(/* PASSWORD ENCODER */);
        $user = $userFactory->create($userDto);

        //Then
        $this->assertInstanceOf(User::class, $user);
    }
}
namespace App\Service\Factory;

use App\Entity\User;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;

class UserFactory
{
    /**
     * @var UserPasswordEncoderInterface
     */
    private $encoder;

    public function __construct(UserPasswordEncoderInterface $encoder)
    {
        $this->encoder = $encoder;
    }

    public function create(UserDto $dto)
    {
        $user = new User();
        $user->setPassword($this->encoder->encodePassword($dto->plainPassword));
        /**
         * CODE..
         */
        return $user;
    }
}

这是对的吗?我如何测试这个有依赖关系的工厂? 我不能在我的 TestClass 中使用 __construct

【问题讨论】:

  • 如果您想知道,用户编码器的第一个参数必须是 User 的实例。编码器只使用它来获取用户类,所以它实际上是什么并不重要。使用这种方法可能会更干净:stackoverflow.com/questions/62980930/…,但结果是一样的。

标签: php symfony phpunit factory


【解决方案1】:

(提示:显然您使用的 UserPasswordEncoderInterface 错误,因为它不是 PasswordEncoderInterface - 前者还期望 User 作为参数,而后者没有,我今天刚刚学到,您可能想解决这个问题)

通常,您必须为要测试的类提供所有依赖项。本质上有不同的方法:实现接口的简化版本(如果它是接口),实际找到依赖项并实例化它或使用模拟并告诉它会发生什么。如果您通常知道依赖项会发生什么(非黑盒测试),则可以完成后者。

所以,你可以用一些定义明确的行为来模拟接口:

//$upe = $this->getMockBuilder(\Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface::class)->getMock();
$upe = $this->getMockBuilder(\Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface::class)->getMock();
$upe->expects($this->any())
            ->method('encodePassword')
            // omit following line, if you don't want to check for param
            ->with($this->equalTo('plainPassword')) 
            ->willReturn('encodedPassword');

然后将该模拟提供给您的用户工厂:

$userfactory = new UserFactory($upe);

这在某种程度上取决于 UserFactory 的实现,期望它本质上以指定的方式被调用,并且总是返回相同的字符串。 (您可以更深入地模拟和/或自己实现接口以获得更多控制权)

【讨论】:

  • 您需要模拟 EncoderFactory,然后让它返回一个模拟的 PasswordEncoder。不管你怎么做都会很痛苦。
  • @Cerad:你确定吗?没有看到任何工厂被用来请求编码器,在模拟上调用 encodePassword 应该工作正常吗?
  • 我猜这是可行的。谢谢我会记住这个解决方案无论如何我还有另一个问题。这一行: $user = $userFactory->create($userDto);创建方法需要 UserDto 类,但我的模拟正在制作 MockClass,我遇到了这个问题 传递给 App\Service\Factory\UserFactory::create() 的参数 1 必须是 App\Service\Factory\UserDto 的实例,Mock_UserDto_b5c48bbf 的实例给定 How我能解决这个问题吗?
  • 我忘了添加我是如何做这个模拟的:$userDto = $this->getMockBuilder(UserDto::class)->getMock();
  • @Jakumi UserPasswordEncoderInterface (形成原始问题)使用 EncoderFactory 为给定的 User 类获取特定的 PasswordEncoder。您的模拟代码至少需要检查两个参数是否传递给 encodePassword。第一个是用户对象。但我认为我只是在这里混淆了这个问题并且会退出。
猜你喜欢
  • 2016-02-06
  • 1970-01-01
  • 2012-12-19
  • 2016-01-11
  • 1970-01-01
  • 1970-01-01
  • 2015-05-12
  • 2016-11-24
  • 2021-06-12
相关资源
最近更新 更多