【问题标题】:Attach the user in session to the current EntityManager when writing functional tests编写功能测试时将会话中的用户附加到当前的 EntityManager
【发布时间】:2016-08-02 18:32:13
【问题描述】:

我正在为与User 实体有关系的Action 实体编写功能测试:

<?php

namespace Acme\AppBundle\Entity;

/**
 * Class Action
 *
 * @ORM\Table()
 * @ORM\Entity(repositoryClass="Acme\AppBundle\Repository\ActionRepository")
 */
class Action
{
    /**
     * @var int
     *
     * @ORM\Column(type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var \Acme\AppBundle\Entity\User
     *
     * @ORM\ManyToOne(targetEntity="\Acme\AppBundle\Entity\User", inversedBy="actions")
     * @ORM\JoinColumn(name="user_id", referencedColumnName="id")
     */
    private $createdBy;
}

用户:

namespace Acme\AppBundle\Entity;

/**
 * @ORM\Entity
 * @ORM\Table(name="`user`")
 */
class User extends BaseUser
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @var ArrayCollection
     *
     * @ORM\OneToMany(targetEntity="Action", mappedBy="createdBy")
     */
    private $actions;
}

并且用户在控制器中设置了以下sn-p:

<?php

namespace Acme\ApiBundle\Controller;

/**
 *
 * @Route("/actions")
 */
class ActionController extends FOSRestController
{
    public function postAction(Request $request)
    {
        $action = new Action();
        $action->setCreatedBy($this->getUser());

        return $this->processForm($action, $request->request->all(), Request::METHOD_POST);
    }
}

例如,当使用 REST 客户端调用操作时,一切正常,ActionUser 之间的关系正确保持。

现在,当使用功能测试测试操作时,由于以下错误,关系不起作用:

通过关系“Acme\AppBundle\Entity\Action#createdBy”找到了一个新实体,该实体未配置为对实体进行级联持久化操作:test。解决此问题:在此未知实体上显式调用 EntityManager#persist() 或配置级联在映射中保持此关联,例如 @ManyToOne(..,cascade={"persist"})。

对于我的功能测试,我需要注入 JWT 和会话令牌,因为我的路由受 JWT 保护,并且我需要有一个用户在会话中。

我是这样注入的:

<?php

namespace Acme\ApiBundle\Tests;

class ApiWebTestCase extends WebTestCase
{
    /**
     * @var ReferenceRepository
     */
    protected $fixturesRepo;

    /**
     * @var Client
     */
    protected $authClient;

    /**
     * @var array
     */
    private $fixtures = [];

    protected function setUp()
    {
        $fixtures = array_merge([
            'Acme\AppBundle\DataFixtures\ORM\LoadUserData'
        ], $this->fixtures);

        $this->fixturesRepo = $this->loadFixtures($fixtures)->getReferenceRepository();

        $this->authClient = $this->createAuthenticatedClient();
    }

    /**
     * Create a client with a default Authorization header.
     *
     * @return \Symfony\Bundle\FrameworkBundle\Client
     */
    protected function createAuthenticatedClient()
    {
        /** @var User $user */
        $user = $this->fixturesRepo->getReference('user-1');

        $jwtManager = $this->getContainer()->get('lexik_jwt_authentication.jwt_manager');
        $token = $jwtManager->create($user);

        $this->loginAs($user, 'api');

        $client = static::makeClient([], [
            'AUTHENTICATION' => 'Bearer ' . $token,
            'CONTENT_TYPE' => 'application/json'
        ]);

        $client->disableReboot();

        return $client;
    }
}

现在,问题是注入的UsernamePasswordToken 包含一个与当前EntityManager 分离的User 实例,从而导致上述Doctrine 错误。

我可以将postAction 方法中的$user 对象合并到EntityManager 中,但我不想这样做,因为这意味着我修改了我的工作代码以使测试通过。 我还尝试将测试中的 $user 对象直接合并到 EntityManager 中,如下所示:

$em = $client->getContainer()->get('doctrine')->getManager();
$em->merge($user);

但它也不起作用。

所以现在,我被卡住了,我真的不知道该怎么办,除了我需要将会话中的用户附加回当前的EntityManager

【问题讨论】:

    标签: symfony testing doctrine-orm functional-testing


    【解决方案1】:

    感谢您的推文,我来完成给定的答案并(尝试)提出解决方案,

    问题是您的用户不是由 EntityManager 管理的,更简单地说,因为它不是在数据库中注册的真实现有用户。

    要解决此问题,您需要有一个真实的(受管理的)用户,该原则可以用于您的操作试图创建的关联。

    因此,您可以在每次执行功能测试用例时创建此用户(并在完成后将其删除),或者在新环境中首次执行测试用例时仅创建一次。

    这样的事情应该可以解决问题:

    /** @var EntityManager */
    private $em;
    
    /**
     */
    public function setUp()
    {
        $client = static::createClient();
        $this->em = $client->getKernel()
            ->getContainer()
            ->get('doctrine');
    
        $this->authClient = $this->createAuthenticatedClient();
    }
    
    /**
     */
    protected function createAuthenticatedClient()
    {
        /** @var User $user */
        $user = $this->em
            ->getRepository('Acme\AppBundle\Entity\User')
            ->findOneBy([], ['id' => DESC]; // Fetch the last created
    
        // ...        
    
        return $client;
    }
    

    遗憾的是您的灯具(更性感),但我看不出有任何方法可以将您的灯具附加为真正的条目,因为您无法与测试控制器进行更多交互。

    另一种方法是向您的登录端点创建一个请求,但这会更难看。

    【讨论】:

    • 你是对的,@adsc 也是。我接受他的回答的原因是因为它是在前一天解决我的问题,即使你更好。对此感到抱歉。
    • 没问题@DevAntoine。我不确定了解 adsc 给出的方式。您是否通过获取真实用户(而不是固定装置)来解决此问题?
    • 是的,我正在使用客户端的容器来获取从夹具加载的用户。
    【解决方案2】:

    您收到的错误消息表明包含在测试客户端容器中的 EntityManager 不知道您的用户实体。这让我相信您在 createAuthenticatedClient 方法中检索用户的方式是使用不同的 EntityManager。

    我建议您尝试使用测试内核的 EntityManager 来检索用户实体。例如,您可以从测试客户端的容器中获取它。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-10-02
      • 1970-01-01
      • 2017-07-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-09
      • 2020-12-06
      相关资源
      最近更新 更多