我创建了一个AuditLoggerBundle *,它有一个使用Doctrine Events(prePersist,preUpdate和preRemove)的服务,以便在audit_log表(AuditLog Entity)中创建一个新条目 .
该捆绑包与我的其他捆绑包工作正常,但我想 unit test it and functional test it .
问题是,为了对 AuditLoggerListener
函数进行功能测试,我需要至少有两个"fake"实体,我可以坚持,更新等 .
在这个包中我不知道如何做到这一点,因为我只有一个AuditLog实体,我需要使用两个over实体( that will be only used in tests ) .
-
第一个实体将是"auditable"(如果我在此实体上执行持久化,更新或删除,我必须在audit_log中有一个新条目) .
-
第二个将是"non-auditable"(当我在此实体上执行持久化,更新或删除时,我不能在audit_log表中有新条目) . *
-
这两个实体可以与唯一的EntityClass相关,但不能是AuditLog的实例
这就是我看到持续功能测试的方式:
<?php
$animal = new Animal(); //this is a fake Auditable entity
$animal->setName('toto');
$em = new EntityManager(); //actually I will use the container to get this manager
$em->persist($animal);
$em->flush();
//Here we test that I have a new line in audit_log table with the right informations
所以我的问题是我的捆绑包中没有任何Animal实体,我只需要这个来测试捆绑包,所以它必须只在测试数据库中创建,而不是在 生产环境 环境中创建(当我做 app/console doctrine:schema:update --force
时)
EDIT_1: After reading your answers, Unit Tests on AuditLoggerListener functions are going to be performed but I still want to make functional tests
*是的我知道它们有很多,但它们并不符合我的要求 .
感谢您的回答,我希望它能帮助一些人!
EDIT_2: here is the code 服务:
services:
#add a prefix to the auditLogger table
kali_audit_logger.doctrine.table.prefix:
class: Kali\AuditLoggerBundle\EventListener\TablePrefixListener
arguments: [%application.db.table.prefix%]
tags:
- { name: doctrine.event_listener, event: loadClassMetadata }
#audit all doctrine actions made by a user
kali_audit_logger.doctrine.event.logger:
class: Kali\AuditLoggerBundle\EventListener\AuditLoggerListener
arguments: [@kali_audit_log, @jms_serializer.serializer, @security.token_storage, %application.auditable.entities%, %application.non.auditable.entities%]
tags:
- { name: doctrine.event_listener, event: prePersist }
- { name: doctrine.event_listener, event: preUpdate }
- { name: doctrine.event_listener, event: preRemove }
# new AuditLog
kali_audit_log:
class: Kali\AuditLoggerBundle\Entity\AuditLog
监听器:
namespace Kali\AuditLoggerBundle\EventListener;
use DateTime;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Event\PreUpdateEventArgs;
use JMS\Serializer\SerializerInterface;
use Kali\AuditLoggerBundle\Entity\AuditLog;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
/**
* Class AuditLoggerListener
* insert a new entry in audit_log table for every doctrine event
*
* @package Kali\AuditLoggerBundle\EventListener
*/
class AuditLoggerListener
{
/**
* @var TokenStorage
*/
protected $securityToken;
/**
* @var EntityManager
*/
protected $em;
/**
* @var array
*/
protected $auditableEntities;
/**
* @var array
*/
protected $nonAuditableEntities = ['Kali\AuditLoggerBundle\Entity\AuditLog'];
/**
* @var AuditLog
*/
protected $auditLogger;
/**
* @var SerializerInterface
*/
protected $serializer;
/**
* @param AuditLog $auditLogger
* @param SerializerInterface $serializer
* @param TokenStorage $securityToken
* @param array $auditableEntities
* @param array $nonAuditableEntities
*/
public function __construct(
AuditLog $auditLogger,
SerializerInterface $serializer,
TokenStorage $securityToken,
$auditableEntities = [],
$nonAuditableEntities = []
) {
$this->auditLogger = $auditLogger;
$this->serializer = $serializer;
$this->securityToken = $securityToken;
$this->auditableEntities = $auditableEntities;
//add all non auditable entities to the current array of non auditable entities
array_merge($this->nonAuditableEntities, $nonAuditableEntities);
}
/**
*
* @param LifecycleEventArgs $args
*
* @return boolean
*/
public function prePersist(LifecycleEventArgs $args)
{
$this->em = $args->getEntityManager();
$entity = $args->getEntity();
$this->em
->getEventManager()
->removeEventListener('prePersist', $this);
if ($this->isAuditableEntity($entity)) {
$this->addAudit(
$this->securityToken->getToken()->getUsername(),
"INSERT",
get_class($entity),
$this->serializer->serialize($entity, JsonEncoder::FORMAT)
);
}
return true;
}
/**
*
* @param PreUpdateEventArgs $args
*
* @return boolean
*/
public function preUpdate(PreUpdateEventArgs $args)
{
$this->em = $args->getEntityManager();
$entity = $args->getEntity();
$this->em
->getEventManager()
->removeEventListener('preUpdate', $this);
if ($this->isAuditableEntity($entity)) {
$this->addAudit(
$this->securityToken->getToken()->getUsername(),
"UPDATE",
get_class($entity),
$this->serializer->serialize($entity, JsonEncoder::FORMAT),
$this->serializer->serialize($args->getEntityChangeSet(), JsonEncoder::FORMAT)
);
}
return true;
}
/**
*
* @param LifecycleEventArgs $args
*
* @return boolean
*/
public function preRemove(LifecycleEventArgs $args)
{
$this->em = $args->getEntityManager();
$entity = $args->getEntity();
$this->em
->getEventManager()
->removeEventListener('preRemove', $this);
if ($this->isAuditableEntity($entity)) {
$this->addAudit(
$this->securityToken->getToken()->getUsername(),
"REMOVE",
get_class($entity),
$this->serializer->serialize($entity, JsonEncoder::FORMAT)
);
}
return true;
}
/**
* Insert a new line in audit_log table
*
* @param string $user
* @param string $action
* @param string $entityClass
* @param null|string $entityValue
* @param null|string $entityChange
*
* @return void
*/
private function addAudit($user, $action, $entityClass, $entityValue = null, $entityChange = null)
{
if ($this->auditLogger) {
$this->auditLogger
->setUser($user)
->setAction($action)
->setEntityClass($entityClass)
->setEntityValue($entityValue)
->setEntityChange($entityChange)
->setDate(new DateTime());
}
if ($this->em) {
$this->em->persist($this->auditLogger);
$this->em->flush();
}
}
/**
* check if an entity is auditable
*
* @param $entity
*
* @return bool
*/
private function isAuditableEntity($entity)
{
$auditable = false;
//the entity must not be in the non auditable entity array
if (!in_array(get_class($entity), $this->nonAuditableEntities)
&& (empty($this->auditableEntities) || (!empty($this->auditableEntities) && in_array(get_class($entity), $this->auditableEntities)))
) {
$auditable = true;
}
return $auditable;
}
}
我想测试这个监听器的preXXXX函数......所以,例如,我需要测试当我在 fake entity (我真的不知道如何模拟)上执行持久化时,有一个新的条目我的audit_log表...
2 回答
测试php类的单元意味着只测试此类包含的代码而无需任何外部交互 . 所以你应该模拟所有外部服务:参见phpunit mock documentation https://phpunit.de/manual/current/en/test-doubles.html#test-doubles.mock-objects
例如,如果您的类看起来像这样:
您的测试应如下所示:
有更多易于使用的模拟框架,如预言(https://github.com/phpspec/prophecy),但他们可能需要更多时间来处理它们 .
以下是与侦听器相关的测试类(类的100%覆盖率):
如果您对此有任何意见,请发表评论:)(例如我可以将"mock all needed objects"部分重构为一个函数)