我创建了一个 AuditLoggerBundle*,它有一个使用 Doctrine Events(prePersist、preUpdate 和 preRemove)的服务,以便在 audit_log 表(AuditLog 实体)中创建一个新条目。
该 bundle 与我的其他 bundle 一起工作正常,但我想对其进行单元测试和功能测试。
问题是,为了对 AuditLoggerListener 函数进行功能测试,我需要至少有两个可以持久化、更新等的“假”实体。
在这个包中,我不知道如何执行此操作,因为我只有一个 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:阅读您的回答后,将对 AuditLoggerListener 函数执行单元测试,但我仍想进行功能测试
*是的,我知道有很多,但它们不符合我的要求。
感谢您的回答,希望对一些人有所帮助!
EDIT_2:这是代码 服务:
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功能... 因此,例如,我需要测试当我对一个假实体(我真的不知道如何模拟)进行持久化时,我的 audit_log 表中是否有一个新条目.. .
最佳答案
it's almost not possible to do functional tests on a shared bundle, because you can't rely on the Symfony2 distribution. I think in this case the best thing to do is properly Unit Test your bundle. – olaurendeau
这里是监听器相关的测试类(100%覆盖该类):
<?php
namespace Kali\AuditLoggerBundle\Tests\Controller;
use Kali\AuditLoggerBundle\Entity\AuditLog;
use Kali\AuditLoggerBundle\EventListener\AuditLoggerListener;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
/**
* Class AuditLoggerListenerTest
* @package Kali\AuditLoggerBundle\Tests\Controller
*/
class AuditLoggerListenerTest extends WebTestCase
{
protected static $container;
/**
* This method is called before the first test of this test class is run.
*
* @since Method available since Release 3.4.0
*/
public static function setUpBeforeClass()
{
self::$container = static::createClient()->getContainer();
}
/*
* ===========================================================================
* TESTS ON AUDITABLE ENTITIES
* ===========================================================================
*/
/**
* test prepersist function
*/
public function testPrePersistWithAuditableEntity()
{
//Mock all the needed objects
$token = $this->mockToken();
$tokenStorage = $this->mockTokenStorage();
$eventManager = $this->mockEventManager();
$entityManager = $this->mockEntityManager();
$entity = $this->mockEntity();
$lifeCycleEvent = $this->mockEvent('LifecycleEventArgs');
//assert the methods that must be called or not
$token -> expects($this->once())->method('getUsername');
$tokenStorage -> expects($this->once())->method('getToken')->willReturn($token);
$eventManager -> expects($this->once())->method('removeEventListener');
$entityManager -> expects($this->once())->method('getEventManager')->willReturn($eventManager);
$entityManager -> expects($this->once())->method('persist');
$lifeCycleEvent -> expects($this->never())->method('getEntityChangeSet');
$lifeCycleEvent -> expects($this->once())->method('getEntityManager')->willReturn($entityManager);
$lifeCycleEvent -> expects($this->once())->method('getEntity')->willReturn($entity);
//instanciate the listener
$listener = new AuditLoggerListener(
new AuditLog(),
self::$container->get('jms_serializer'),//Yes this is not really good to do that
$tokenStorage
);
// call the function to test
$listener->prePersist($lifeCycleEvent);
}
/**
* test preUpdate function
*/
public function testPreUpdateWithAuditableEntity()
{
//Mock all the needed objects
$token = $this->mockToken();
$tokenStorage = $this->mockTokenStorage();
$eventManager = $this->mockEventManager();
$entityManager = $this->mockEntityManager();
$entity = $this->mockEntity();
$lifeCycleEvent = $this->mockEvent('PreUpdateEventArgs');
//assert the methods that must be called or not
$token -> expects($this->once())->method('getUsername');
$tokenStorage -> expects($this->once())->method('getToken')->willReturn($token);
$eventManager -> expects($this->once())->method('removeEventListener');
$entityManager -> expects($this->once())->method('getEventManager')->willReturn($eventManager);
$entityManager -> expects($this->once())->method('persist');
$lifeCycleEvent -> expects($this->once())->method('getEntityChangeSet');
$lifeCycleEvent -> expects($this->once())->method('getEntityManager')->willReturn($entityManager);
$lifeCycleEvent -> expects($this->once())->method('getEntity')->willReturn($entity);
//instanciate the listener
$listener = new AuditLoggerListener(
new AuditLog(),
self::$container->get('jms_serializer'),//Yes this is not really good to do that
$tokenStorage
);
// call the function to test
$listener->preUpdate($lifeCycleEvent);
}
/**
* test PreRemove function
*/
public function testPreRemoveWithAuditableEntity()
{
//Mock all the needed objects
$token = $this->mockToken();
$tokenStorage = $this->mockTokenStorage();
$eventManager = $this->mockEventManager();
$entityManager = $this->mockEntityManager();
$entity = $this->mockEntity();
$lifeCycleEvent = $this->mockEvent('LifecycleEventArgs');
//assert the methods that must be called or not
$token -> expects($this->once())->method('getUsername');
$tokenStorage -> expects($this->once())->method('getToken')->willReturn($token);
$eventManager -> expects($this->once())->method('removeEventListener');
$entityManager -> expects($this->once())->method('getEventManager')->willReturn($eventManager);
$entityManager -> expects($this->once())->method('persist');
$lifeCycleEvent -> expects($this->never())->method('getEntityChangeSet');
$lifeCycleEvent -> expects($this->once())->method('getEntityManager')->willReturn($entityManager);
$lifeCycleEvent -> expects($this->once())->method('getEntity')->willReturn($entity);
//instanciate the listener
$listener = new AuditLoggerListener(
new AuditLog(),
self::$container->get('jms_serializer'),//Yes this is not really good to do that
$tokenStorage
);
// call the function to test
$listener->preRemove($lifeCycleEvent);
}
/*
* ===========================================================================
* TESTS ON NON AUDITABLE ENTITIES
* ===========================================================================
*/
/**
* test prepersit function
*/
public function testPrePersistWithNonAuditableEntity()
{
//Mock all the needed objects
$token = $this->mockToken();
$tokenStorage = $this->mockTokenStorage();
$eventManager = $this->mockEventManager();
$entityManager = $this->mockEntityManager();
$entity = new AuditLog();//this entity is non Auditable
$lifeCycleEvent = $this->mockEvent('LifecycleEventArgs');
//assert the methods that must be called or not
$token -> expects($this->never())->method('getUsername');
$tokenStorage -> expects($this->never())->method('getToken')->willReturn($token);
$eventManager -> expects($this->once())->method("removeEventListener");
$entityManager -> expects($this->never())->method('persist');
$entityManager -> expects($this->once())->method('getEventManager')->willReturn($eventManager);
$lifeCycleEvent -> expects($this->never())->method('getEntityChangeSet');
$lifeCycleEvent -> expects($this->once())->method('getEntityManager')->willReturn($entityManager);
$lifeCycleEvent -> expects($this->once())->method('getEntity')->willReturn($entity);
$listener = new AuditLoggerListener(
new AuditLog(),
self::$container->get('jms_serializer'),
$tokenStorage
);
$listener->prePersist($lifeCycleEvent);
}
/**
* test prepersit function
*/
public function testPreUpdateWithNonAuditableEntity()
{
//Mock all the needed objects
$token = $this->mockToken();
$tokenStorage = $this->mockTokenStorage();
$eventManager = $this->mockEventManager();
$entityManager = $this->mockEntityManager();
$entity = new AuditLog();//this entity is non Auditable
$lifeCycleEvent = $this->mockEvent('PreUpdateEventArgs');
//assert the methods that must be called or not
$token -> expects($this->never())->method('getUsername');
$tokenStorage -> expects($this->never())->method('getToken')->willReturn($token);
$eventManager -> expects($this->once())->method("removeEventListener");
$entityManager -> expects($this->never())->method('persist');
$entityManager -> expects($this->once())->method('getEventManager')->willReturn($eventManager);
$lifeCycleEvent -> expects($this->never())->method('getEntityChangeSet');
$lifeCycleEvent -> expects($this->once())->method('getEntityManager')->willReturn($entityManager);
$lifeCycleEvent -> expects($this->once())->method('getEntity')->willReturn($entity);
$listener = new AuditLoggerListener(
new AuditLog(),
self::$container->get('jms_serializer'),
$tokenStorage
);
$listener->preUpdate($lifeCycleEvent);
}
/**
* test preRemove function
*/
public function testPreRemoveWithNonAuditableEntity()
{
//Mock all the needed objects
$token = $this->mockToken();
$tokenStorage = $this->mockTokenStorage();
$eventManager = $this->mockEventManager();
$entityManager = $this->mockEntityManager();
$entity = new AuditLog();//this entity is non Auditable
$lifeCycleEvent = $this->mockEvent('LifecycleEventArgs');
//assert the methods that must be called or not
$token -> expects($this->never())->method('getUsername');
$tokenStorage -> expects($this->never())->method('getToken')->willReturn($token);
$eventManager -> expects($this->once())->method("removeEventListener");
$entityManager -> expects($this->never())->method('persist');
$entityManager -> expects($this->once())->method('getEventManager')->willReturn($eventManager);
$lifeCycleEvent -> expects($this->never())->method('getEntityChangeSet');
$lifeCycleEvent -> expects($this->once())->method('getEntityManager')->willReturn($entityManager);
$lifeCycleEvent -> expects($this->once())->method('getEntity')->willReturn($entity);
$listener = new AuditLoggerListener(
new AuditLog(),
self::$container->get('jms_serializer'),
$tokenStorage
);
$listener->preRemove($lifeCycleEvent);
}
/*
* ===========================================================================
* MOCKS
* ===========================================================================
*/
/**
* Mock a Token object
*
* @return \PHPUnit_Framework_MockObject_MockObject
*/
private function mockToken()
{
$token = $this->getMock(
'Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken',
['getUsername'],
[],
'',
false
);
return $token;
}
/**
* Mock a TokenStorage object
*
* @return \PHPUnit_Framework_MockObject_MockObject
*/
private function mockTokenStorage()
{
//mock tokenStorage
$tokenStorage = $this->getMock(
'Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage',
['getToken'],
[],
'',
false
);
return $tokenStorage;
}
/**
* Mock an EventManager Object
*
* @return \PHPUnit_Framework_MockObject_MockObject
*/
private function mockEventManager()
{
//mock the event manager
$eventManager = $this->getMock(
'\Doctrine\Common\EventManager',
['removeEventListener'],
[],
'',
false
);
return $eventManager;
}
/**
* Mock an EntityManager
*
* @return \PHPUnit_Framework_MockObject_MockObject
*/
private function mockEntityManager()
{
//mock the entityManager
$emMock = $this->getMock(
'\Doctrine\ORM\EntityManager',
['getEventManager', 'persist', 'update', 'remove', 'flush'],
[],
'',
false
);
return $emMock;
}
/**
* Mock an Entity Object
*
* @return \PHPUnit_Framework_MockObject_MockObject
*/
private function mockEntity()
{
$entity = $this->getMockBuilder('stdClass')
->setMethods(['getName', 'getType'])
->getMock();
$entity->expects($this->any())
->method('getName')
->will($this->returnValue('toto'));
$entity->expects($this->any())
->method('getType')
->will($this->returnValue('chien'));
return $entity;
}
/**
* mock a lifeCycleEventArgs Object
*
* @param $eventType
*
* @return \PHPUnit_Framework_MockObject_MockObject
*/
private function mockEvent($eventType)
{
$lifeCycleEvent = $this->getMock(
'\Doctrine\ORM\Event\\'.$eventType,
['getEntityManager', 'getEntity', 'getEntityChangeSet'],
[],
'',
false
);
return $lifeCycleEvent;
}
}
如果你对此有什么想说的,请发表评论:) (例如,我可以将“模拟所有需要的对象”部分重构为一个函数)
关于php - 如何在没有任何实体的情况下测试学说 EventListener/Subscriber,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32609772/
出于纯粹的兴趣,我很好奇如何按顺序创建PI,而不是在过程结果之后生成数字,而是让数字在过程本身生成时显示。如果是这种情况,那么数字可以自行产生,我可以对以前看到的数字实现垃圾收集,从而创建一个无限系列。结果只是在Pi系列之后每秒生成一个数字。这是我通过互联网筛选的结果:这是流行的计算机友好算法,类机器算法:defarccot(x,unity)xpow=unity/xn=1sign=1sum=0loopdoterm=xpow/nbreakifterm==0sum+=sign*(xpow/n)xpow/=x*xn+=2sign=-signendsumenddefcalc_pi(digits
我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚
如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby
我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%
我好像记得Lua有类似Ruby的method_missing的东西。还是我记错了? 最佳答案 表的metatable的__index和__newindex可以用于与Ruby的method_missing相同的效果。 关于ruby-难道Lua没有和Ruby的method_missing相媲美的东西吗?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/7732154/
exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby中使用两个参数异步运行exe吗?我已经尝试过ruby命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何rubygems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除
我有一个奇怪的问题:我在rvm上安装了rubyonrails。一切正常,我可以创建项目。但是在我输入“railsnew”时重新启动后,我有“程序'rails'当前未安装。”。SystemUbuntu12.04ruby-v"1.9.3p194"gemlistactionmailer(3.2.5)actionpack(3.2.5)activemodel(3.2.5)activerecord(3.2.5)activeresource(3.2.5)activesupport(3.2.5)arel(3.0.2)builder(3.0.0)bundler(1.1.4)coffee-rails(
这是在Ruby中设置默认值的常用方法:classQuietByDefaultdefinitialize(opts={})@verbose=opts[:verbose]endend这是一个容易落入的陷阱:classVerboseNoMatterWhatdefinitialize(opts={})@verbose=opts[:verbose]||trueendend正确的做法是:classVerboseByDefaultdefinitialize(opts={})@verbose=opts.include?(:verbose)?opts[:verbose]:trueendend编写Verb
鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende
我正在为一个项目制作一个简单的shell,我希望像在Bash中一样解析参数字符串。foobar"helloworld"fooz应该变成:["foo","bar","helloworld","fooz"]等等。到目前为止,我一直在使用CSV::parse_line,将列分隔符设置为""和.compact输出。问题是我现在必须选择是要支持单引号还是双引号。CSV不支持超过一个分隔符。Python有一个名为shlex的模块:>>>shlex.split("Test'helloworld'foo")['Test','helloworld','foo']>>>shlex.split('Test"