class CreateControllerTest extends TestCaseWithConsecutiveMock
{
public function testInvokeWithEmptyRequest(): void
{
$controller = new class(
$this->generateMock(UserLimitsServiceInterface::class),
$this->generateMock(TokenStorage::class),
$this->generateMock(Security::class),
new IsEventDumpRequestExpiredQueryHandler(),
) extends CreateController {
protected function denyAccessUnlessGranted($attribute, $subject = null, string $message = 'Access Denied.'): void
{
return;
}
protected function isGranted($attribute, $subject = null): bool
{
return true;
}
};
}
}
I have this piece of code where I'm testing the CreateController class, and I use an abstract class here to "silence" a few methods that have dependencies that are very hard to mock.
Everything works fine, except for one thing. Whenever in my PhpStorm I Ctrl + Click on one of these methods in the CreateController.php to go to the method definition, PhpStorm jumps to the anonymous class, not to the parent class.
Is there a way to exclude a method of a class from indexing?
Ideally
..........
extends CreateController {
/** #exclude-from-index */
protected function denyAccessUnlessGranted($attribute, $subject = null, string $message = 'Access Denied.'): void
{
return;
}
}
Related
I am trying to write a unit test (using phpunit and mockery) for a class that uses a Propel query.
How do I mock the query $contact = ClientContactQuery::create()->findPK($id);
I am struggling to find any examples for this.
My class;
<?php
namespace MyBundle\Classes;
use MyBundle\Model\ClientContactQuery;
use MyBundle\Model\ClientContact;
class Contacts {
protected $_cache;
public function __construct($cache)
{
$this->_cache = $cache;
}
public function getContact($id)
{
$contact = ClientContactQuery::create()->findPK($id);
if (! $contact) {
throw new NotFoundHttpException('Client contact not found.');
}
return $contact;
}
}
My test case so far;
<?php
namespace MyBundle\Tests\Classes;
use Mockery as m;
use MyBundle\Classes\Contacts as c;
class ContactsTest extends \PHPUnit_Framework_TestCase
{
public function tearDown()
{
m::close();
}
public function testGetValidContact()
{
// Arrange
$cache = m::mock('cache');
// Act
$contact = new c($cache);
// am lost at this point :-(
// Assert
$this->assertInstanceOf('MyBundle\Classes\Contacts', $contact);
}
}
Static functions do not play nice with unit testing, and please do not create a private method and mock it.
I'd highly suggest creating a Query Factory. Not only this will give you ability to inject and unit test your code, but it will make life easier if you want to use XYZ orm instead Propel in the future.
#
<?php
namespace MyBundle\Classes;
use MyBundle\Model\ClientContactQuery;
use MyBundle\Model\ClientContact;
class Contacts {
protected $_cache;
/** #var QueryFactory */
private $queryFactory;
public function __construct( $cache, QueryFactory $queryFactory ) {
$this->_cache = $cache;
$this->queryFactory = $queryFactory;
}
public function getContact( $id ) {
$contact = $this->queryFactory->newClientContactQuery()->findPK($id);
if (! $contact) {
throw new NotFoundHttpException('Client contact not found.');
}
return $contact;
}
}
#
<?php
class QueryFactory {
const CLASS_NAME = __CLASS__;
public function newClientContactQuery() {
return ClientContactQuery::create();
}
public function newSomeOtherQuery() {
return SomeOtherQuery::create();
}
}
#
<?php
namespace MyBundle\Tests\Classes;
use Mockery as m;
use MyBundle\Classes\Contacts as c;
class ContactsTest extends \PHPUnit_Framework_TestCase {
public function tearDown() {
m::close();
}
public function testGetValidContact() {
$cache = m::mock( 'cache' );
$queryFactory = m::mock( QueryFactory::CLASS_NAME );
$clientContactQuery = m::mock( 'ClientContanctQuery' );
$contact = new c($cache, $queryFactory);
$queryFactory->shouldReceive('newClientContactQuery')->with()->once()->andReturn( $clientContactQuery );
$clientContactQuery->shouldReceive('findPK')->with('myTestInputId')->once->andReturn('something?');
$this->assertInstanceOf('MyBundle\Classes\Contacts', $contact);
}
}
You really can`t mock it, because you have "hard" dependency on it. So, to solve this problem, you should consider to move "hard" dependency on query from getContact method.
You can do it in three ways:
Create your private method, e. g. "getQueryFindByPk", and then mock it, on your Contacts class, to return what you need.
Pass query instance to constructor, but as I understand, you can have multiple query instances.
Create something like QueryFactory, Repository or QueryBuilder, which can return to you instance of query.
So, once again, problem is in having "hard" dependency on query.
I'm having strange problems when trying to persist a class of User that has a reference to many UserProperties. Note that a UserProperty will be managed by a cascade:persist.
UserProperties itself has a reference to a Property.
When creating a new User with a new UserProperty (which itself has a reference to an existing Property) it throws strange (strange as in i didn't expect it) error:
InvalidArgumentException: A new entity was found through the relationship 'UserProperty#property' that was not configured to cascade persist operations for entity
User:
class User extends IdentifiableObject {
// … other vars
/**
* #OneToMany(targetEntity="UserProperty", mappedBy="user", cascade={"persist", "remove"}, orphanRemoval=true)
*/
private $userProperties = null;
public function __construct() {
$this->userProperties = new ArrayCollection();
}
// … other methods
public function getUserProperties() {
return $this->userProperties;
}
public function setUserProperties($userProperties) {
$this->userProperties = $userProperties;
}
public function addUserProperty(UserProperty $userProperty) {
$userProperty->setUser($this);
$this->userProperties[] = $userProperty;
}
}
UserProperty:
class UserProperty extends IdentifiableObject {
/**
* #OneToOne(targetEntity="Property")
* #JoinColumn(name="propertyID")
*/
private $property;
public function getProperty() {
return $this->property;
}
public function setProperty($property) {
$this->property = $property;
}
}
Property class has no references to either class.
And finally my testClass using PHPUnit:
class UserDaoTest extends PHPUnit_Framework_TestCase {
private static $userDao;
private static $propertyDao;
public static function setUpBeforeClass() {
//this will make the EntityManager called inside our DAOImpl point to our test database...
define('__DBNAME__', 'db_test');
createCleanTestDatabase();
self::$userDao = new UserDaoImpl();
self::$propertyDao = new PropertyDaoImpl();
}
public function testEntityClassVariable() {
$this->assertEquals("User", self::$userDao->getEntityClass());
}
public function testPersistUserWithoutProperties() {
$user = new User();
$user->setUserName("tester1");
$user->setUserType(1);
self::$userDao->persist($user);
self::$userDao->flush();
$this->assertEquals(1, count(self::$userDao->findAll()));
}
public function testPersistUserWithProperties() {
$user = new User();
$user->setUserName("tester2");
$user->setUserType(1);
$property = new Property();
$property->setName("propertyName");
$property->setType(1);
self::$propertyDao->persist($property);
self::$propertyDao->flush();
$userProperty = new UserProperty();
$userProperty->setProperty($property);
$userProperty->setValue("test");
$user->addUserProperty($userProperty);
self::$userDao->persist($user);
self::$userDao->flush();
$this->assertEquals(2, count(self::$userDao->findAll()));
$userInDB = self::$userDao->find($user);
$this->assertNotNull($userInDB);
$this->assertEquals(1, count($userInDB->getUserProperties()));
}
}
The strange thing is that the Property is indeed created in the Database.
Also the test works perfectly fine IF i use the userDao->persist to save the Property (instead of the propertyDao...
Any help would be appreciated, thanks in advance!
The problem was that i was using a different entityManager in each dao so effectively having a different UnitOfWork for each DAO. When i made the entity a singleton so that each DAO had the same reference to it.
I am writing some tests for a Magento module, using Ivan Chepurnyi's extension, and I'm having trouble using the mock objects.
Here is the class:
<?php
class Namespace_Module_Block_Class extends Mage_Core_Block_Template
{
private $_salesCollection;
public function __construct()
{
$this->_salesCollection = Mage::getModel('module/classA')->getCollection()
->addFieldToFilter('id', $this->_getId());
}
public function _getId()
{
return Mage::getModel('module/classB')->getId();//session params
}
public function getSalesTotalNumber()
{
return $this->_salesCollection->count();
}
}
The method I'm trying to test is getSalesTotalNumber().
And here is the test:
<?php
class Namespace_Module_Test_Block_Class extends EcomDev_PHPUnit_Test_Case
{
private $_mock;
public function setUp()
{
$this->_mock = $this->getMock('Namespace_Module_Block_Class',
array('_getId')
);
$this->_mock->expects($this->any())
->method('_getId')
->will($this->returnValue(1024));
parent::setUp();
}
/**
* #test
* #loadFixture
* #loadExpectation
*/
public function testSalesTotalNumber()
{
$actual = $this->_mock->getSalesTotalValue();
$expected = $this->_getExpectations()->getSalesTotalNumber();
$this->assertEquals($expected, $actual);
}
}
As you can see, what I want to do is overwrite the _getId() method so that it returns an id which match the id in the fixture and so load the collection. But it doesn't work :-(.
In my test, if I echo $this->_mock->_getId() it returns the correct Id (1024). But in the __construct() of my class $this->_getId() returns null, which is the expected value during testing (I mean, during testing there is no session, so it can't get the object's Id as I store it in a session variable). So the _getId() method isn't mocked by my test case.
Any help will be highly appreciated.
So my problem was not in the mock/test but in the class.
I have moved the content of __construct() into a protected method which returns the collection object. That's how my class looks like now:
<?php
class Namespace_Module_Block_Class extends Mage_Core_Block_Template
{
private $_salesCollection;
protected function _getAffiliateSales()
{
if (is_null($this->_salesCollection)) {
$affiliateId = $this->_getId();
$this->_salesCollection = Mage::getModel('module/classA')
->addFieldToFilter('id', $affiliateId);
}
return $this->_salesCollection;
}
public function _getId()
{
return Mage::getModel('module/classB')->getId();//session params
}
public function getSalesTotalNumber()
{
return $this->_getAffiliateSales()->count();
}
}
How does one stub a method in PHPUnit that is called by the class under test's constructor? The simple code below for example won't work because by the time I declare the stubbed method, the stub object has already been created and my method called, unstubbed.
Class to test:
class ClassA {
private $dog;
private $formatted;
public function __construct($param1) {
$this->dog = $param1;
$this->getResultFromRemoteServer();
}
// Would normally be private, made public for stubbing
public getResultFromRemoteServer() {
$this->formatted = file_get_contents('http://whatever.com/index.php?'.$this->dog);
}
public getFormatted() {
return ("The dog is a ".$this->formatted);
}
}
Test code:
class ClassATest extends PHPUnit_Framework_TestCase {
public function testPoodle() {
$stub = $this->getMockBuilder('ClassA')
->setMethods(array('getResultFromRemoteServer'))
->setConstructorArgs(array('dog52'))
->getMock();
$stub->expects($this->any())
->method('getResultFromRemoteServer')
->will($this->returnValue('Poodle'));
$expected = 'This dog is a Poodle';
$actual = $stub->getFormatted();
$this->assertEquals($expected, $actual);
}
}
Use disableOriginalConstructor() so that getMock() won't call the constructor. The name is a bit misleading because calling that method ends up passing false for $callOriginalConstructor. This allows you to set expectations on the returned mock before calling the constructor manually.
$stub = $this->getMockBuilder('ClassA')
->setMethods(array('getResultFromRemoteServer'))
->disableOriginalConstructor()
->getMock();
$stub->expects($this->any())
->method('getResultFromRemoteServer')
->will($this->returnValue('Poodle'));
$stub->__construct('dog52');
...
The problem is not the stubbing of the method, but your class.
You are doing work in the constructor. In order to set the object into state, you fetch a remote file. But that step is not necessary, because the object doesn't need that data to be in a valid state. You dont need the result from the file before you actually call getFormatted.
You could defer loading:
class ClassA {
private $dog;
private $formatted;
public function __construct($param1) {
$this->dog = $param1;
}
protected getResultFromRemoteServer() {
if (!$this->formatted) {
$this->formatted = file_get_contents(
'http://whatever.com/index.php?' . $this->dog
);
}
return $this->formatted;
}
public getFormatted() {
return ("The dog is a " . $this->getResultFromRemoteServer());
}
}
so you are lazy loading the remote access to when it's actually needed. Now you dont need to stub getResultFromRemoteServer at all, but can stub getFormatted instead. You also won't need to open your API for the testing and make getResultFromRemoteServer public then.
On a sidenote, even if it's just an example, I would rewrite that class to read
class DogFinder
{
protected $lookupUri;
protected $cache = array();
public function __construct($lookupUri)
{
$this->lookupUri = $lookupUri;
}
protected function findById($dog)
{
if (!isset($this->cache[$dog])) {
$this->cache[$dog] = file_get_contents(
urlencode($this->lookupUri . $dog)
);
}
return $this->cache[$id];
}
public function getFormatted($dog, $format = 'This is a %s')
{
return sprintf($format, $this->findById($dog));
}
}
Since it's a Finder, it might make more sense to actually have findById public now. Just keeping it protected because that's what you had in your example.
The other option would be to extend the Subject-Under-Test and replace the method getResultFromRemoteServer with your own implementation returning Poodle. This would mean you are not testing the actual ClassA, but a subclass of ClassA, but this is what happens when you use the Mock API anyway.
As of PHP7, you could utilize an Anonymous class like this:
public function testPoodle() {
$stub = new class('dog52') extends ClassA {
public function getResultFromRemoteServer() {
return 'Poodle';
}
};
$expected = 'This dog is a Poodle';
$actual = $stub->getFormatted();
$this->assertEquals($expected, $actual);
}
Before PHP7, you'd just write a regular class extending the Subject-Under-Test and use that instead of the Subject-Under-Test. Or use disableOriginalConstructor as shown elsewhere on this page.
I have a requirement where a process can be implemented in 2 different situation. One situation would be where the start date cannot be in the past and the other would be where it can.
Currently we utilise Value Objects where we perform a series of a validation items on each field submitted using Zend Validate objects.
The validation extends a base class e.g.
class ValueObject_test1 extends filter()
Filter is made up of: -
class filter {
protected $_filter;
protected $_filterRules = array();
protected $_validatorRules = array();
protected $_data = array();
protected $_objData = array();
protected $_options = array(Zend_Filter_Input::ESCAPE_FILTER => 'StripTags');
protected $_runValidation = true;
protected function _setFilter()
protected function _addFilter()
protected function _addValidator()
protected function _addData()
protected function _addObject()
protected function _addOption()
public function getData()
public function hasErrors()
public function getMessages()
public function getValidationState()
public function __get()
public function __isset()
public function __set()
}
ValueObject_test1 is made up of:
class ValueObject_test1 extends filter {
public function __construct($testVar) {
$this->_setData(testVar);
$this->_setFilters();
$this->_setValidators();
if($this->_runValidation) {
$this->_setFilter();
}
}
protected function _setFilters(){
$this->_addFilter("*", "StringTrim");
}
protected function _setData($testVar) {
$this->_addData('testVar', $testVar);
}
protected function _setValidators() {
$this->_addValidator('testVar', array(new Zend_Validate(), 'presence'=>'required', 'messages'=>'Enter something'));
}
}
What I'm trying to achieve is an extension of ValueObject_test1 so that my second situation will have an additional validation item as well as the items in ValueObject_test1()
I've written the following for my second situation:-
<?php
class ValueObject_test2 extends ValueObject_test1 {
public function __construct($testVar, $testVar2) {
$this->_setData($testVar, $testVar2);
$this->_setFilters();
$this->_setValidators();
if($this->_runValidation) {
$this->_setFilter();
}
}
protected function _setFilters(){
$this->_addFilter("*", "StringTrim");
}
protected function _setData($testVar, $testVar2) {
$this->_addData('testVar', $testVar);
$this->_addData('testVar2', $testVar2);
}
protected function _setValidators() {
$this->_addValidator('testVar2', array(new Zend_Validate(), 'presence'=>'required', 'messages'=>'Enter something'));
}
}
The issue i'm having is that the output from this only appears to validate my second situation validation and nothing on the second. I'm under the impression that by setting both variables in _setData() the validation should occur for items in ValueObject_test1 and the items in my ValueObject_test2?
class ValueObject_test2 extends ValueObject_test1 {
public function __construct($testVar, $testVar2) {
$this->_setData($testVar, $testVar2);
parent::__construct($testVar);
}
// _setFilters is identical to the parent implementation
protected function _setData($testVar, $testVar2) {
$this->_addData('testVar2', $testVar2);
parent::_setData($testVar);
}
protected function _setValidators() {
$this->_addValidator('testVar2', array(new Zend_Validate(), 'presence'=>'required', 'messages'=>'Enter something'));
parent::_setValidators();
}
}
My code doesn't call the _setData correctly because the $this->setData(testVar) inside the parent::_construct's will call the $this->_setData(testVar) version of the function.
If you want to override a function, your override will likely want to also run the logic of the parent.
<?php
class Foo {
protected function _setFoobar() {
// Foo logic
}
}
class Bar extends Foo {
protected function _setFoobar() {
// custom Bar logic specific to the child class (Bar)
parent::_setFoobar();
}
}