在带有Doctrine的Symfony 3中,我试图在数据库中有一个“last updated by”列,只要将更改保存到表中,该列就会自动设置为登录用户的用户名。
我正在使用EventSubscriber
执行此操作,当我使用系统进行实际操作时可以正常工作,但是当我运行单元测试时,它认为没有用户登录。
我遵循how to implement authentication for unit tests上的说明,它提出了两种方法 - 使用Basic auth或设置令牌。这两种方法都允许测试运行,就像用户通过身份验证一样,但它们都不允许我检索登录用户的用户名,因此我无法测试是否正确设置了“last updated by”列。
我究竟做错了什么?还有其他方法我应该在EventSubscriber
中检索登录用户的用户名吗?
这是我的EventSubscriber
实现,它适用于实际请求,但在测试运行时返回空标记:
class BaseEntitySubscriber implements EventSubscriber {
public function getSubscribedEvents() {
return array('prePersist');
}
public function prePersist(LifecycleEventArgs $args) {
$em = $args->getEntityManager();
$token = $this->ts->getToken(); // returns null during tests
$username = $token ? $token->getUsername() : null;
if ($username === null) throw new \Exception("User is not logged in!");
$entity = $args->getEntity();
$entity->setLastUpdateBy($username);
}
}
# services.yml
AppBundle\EventListener\BaseEntitySubscriber:
tags:
- { name: doctrine.event_subscriber, connection: default }
arguments: [ "@security.token_storage" ]
编辑:@dbrumann测试代码如下:(大致)
public function testEditLookup()
{
$idLookup = $this->getFixtureId('lookup.example');
// Get the existing one first so we know if it changes later.
$crawler = $this->client->request('GET', "/lookup/$idLookup");
$lookup = $this->decodeSymfonyJSON($this->client->getResponse());
$this->assertEquals('Old title', $lookup['title']);
$this->assertEquals('system', $lookup['last_update_by']);
$this->assertEquals('Example', $lookup['detail']);
// Make the change.
$crawler = $this->client->request('PATCH',
"/lookup/$idLookup",
array('title' => 'New title')
);
$this->checkSymfonyError($this->client->getResponse());
// Retrieve it again to confirm it is now different.
$crawler = $this->client->request('GET', "/lookup/$idLookup");
$lookup = $this->decodeSymfonyJSON($this->client->getResponse());
$this->assertEquals('New title', $lookup['title']);
$this->assertEquals('testuser', $lookup['last_update_by']);
// Make sure this hasn't changed.
$this->assertEquals('Example', $lookup['detail']);
}
我想我已经解决了这个问题。事实证明 - 假设我以正确的方式检索用户 - 您需要自己设置令牌:
$user = 'testuser';
$pass = 'testpass';
$this->client = static::createClient(array(), array(
'PHP_AUTH_USER' => $user,
'PHP_AUTH_PW' => $pass,
'HTTPS' => true, // Default to HTTPS
));
// Also set a token so the username can be retrieved later
$firewallContext = 'main';
$token = new UsernamePasswordToken($user, $pass, $firewallContext, array('ROLE_ADMIN'));
$ts = $this->client->getContainer()->get('security.token_storage');
$ts->setToken($token);
这是测试用例的setUp()
实现。
编辑:进一步测试还有一件事需要注意。加载灯具时使用UsernamePasswordToken
,运行测试时使用PHP_AUTH_USER
。这意味着您可以将初始数据填充为一个用户,然后让不同的用户运行测试。这样可以确保将“上次更新时间”字段更改为正确的用户名。
如果你像我一样使用Doctrine事件订阅者,那么prePersist
钩子将看到UsernamePasswordToken
用户,而preUpdate
钩子将看到PHP_AUTH_USER
用户。