I'm trying to mock properties but can't get it to work. In this case I'm trying to mock request property Symfony\Component\HttpFoundation\Request.
According to this answer I should return a value for __get
Following code keeps showing NULL:
$testRequest = $this->getMockBuilder('Symfony\Component\HttpFoundation\Request')
->disableOriginalConstructor()
->getMock();
$testRequest->expects($this->any())
->method('__get')
->with($this->equalTo('request'))
->will($this->returnValue("working"));
var_dump($testRequest->request);
Maybe it's because the answer is too old so checking out the current documentation of mocking but that one doesn't mention how to mock properties at all.
According to another answer I can try the following:
private $post=array('search'=>'abc','start'=>null);
...
$paramBag = $this->getMockBuilder('Symfony\Component\HttpFoundation\ParameterBag')
->disableOriginalConstructor()
->getMock();
foreach($this->post as $key=>$value){
echo "setting:".$key.' with:'.$value.PHP_EOL;
$paramBag->expects($this->any())
->method('get')
->with($this->equalTo($key))
->will($this->returnValue($value));
}
$this->request = $this->getMockBuilder('Symfony\Component\HttpFoundation\Request')
->disableOriginalConstructor()
->getMock();
$this->request->request=$paramBag;
echo ($this->request->request->get('start'));
That gives me:
Expectation failed for method name is equal to <string:get> when invoked zero or more times
Parameter 0 for invocation Symfony\Component\HttpFoundation\ParameterBag::get('search', null, false) does not match expected value.
Failed asserting that two strings are equal.
--- Expected
+++ Actual
## ##
-'search'
+'start'
Is it not possible to override multiple methods with different values? Again, the documentation completely lacks any examples of that.
If I try echo ($this->request->request->get('start')); it will fail on expecting search but getting start. There is no way I can give it something that doesn't fail.
Trying to unit test but Symfony2 does not seem to have such a thing. Every documentation almost immediately skips unit testing with examples of ordinary classes needing no dependencies and then moves to functional testing. And some documentation flat out say unit testing for repositories is not recommended.
I would like to see if DQL statements are created correctly though and am left with no documented option to unit test this.
Can do this part by creating a fake Doctrine\ORM\EntityRepository class and include that file at the start of my test. Tried the same with Symfony\Component\HttpFoundation\Request but get a can't re define this class when trying to do that. Somehow when my test is loaded Symfony\Component\HttpFoundation\Request already is loaded.
Not entirely sure how you mean mock properties, I would assume properties are set via getters and setters and then you could just set them with whatever Mock you want.
If however you need to assign a mock object to a private /protected property for which you do not have an accessor method, you can do this:
$reflection = new \ReflectionClass($objectThatContainsThePropertyToMock);
$property = $reflection->getProperty($propertyName);
$property->setAccessible(true);
return $property->setValue($mockedObject);
Related
Suppose I have a class with a private property and associated public getter and setter. I want to test with PHPUnit that the property gets the correct value after the setter has been used or that the getter returns the correct property.
Of course I can test the setter by using the getter to see that the object is storing the correct value, and vice versa for testing the getter. However, this doesn't guarantee that the private property is the one being set.
Say I had the following class. I created a property, getter and setter. But I made a typo in the property name, so the getter and the setter don't actually manipulate the property they're meant to manipulate
class SomeClass
{
private
$mane = NULL; // Was supposed to be $name but got fat-fingered!
public function getName ()
{
return ($this -> name);
}
public function setName ($newName)
{
$this -> name = $newName;
return ($this);
}
}
If I run the following test
public function testSetName ()
{
$this -> object -> setName ('Gerald');
$this -> assertTrue ($this -> object -> getName () == 'Gerald');
}
I would get a pass. However, something very bad has actually happened that I don't want. When setName() is called, it actually creates a new property in the class with the name I thought my private property had, only the one that the setter creates is public! I can demonstrate that with the following code:
$a = new SomeClass;
$a -> setName('gerald');
var_dump ($a -> getName ());
var_dump ($a -> name);
It would output:
string(6) "gerald"
string(6) "gerald"
Is there any way I can access the private properties from PHPUnit so I can write tests that make sure that the properties I think are being get and set actually really are being get and set?
Or is there some other thing I should be doing in a test to catch problems like this without trying to get access to the private state of the object under test?
You can also use Assert::assertAttributeEquals('value', 'propertyName', $object).
See https://github.com/sebastianbergmann/phpunit/blob/3.7/PHPUnit/Framework/Assert.php#L490
For testing properties, I'd make the same arguments I make then talking about testing private methods.
You usually don't want to do this.
It's about testing observable behavior.
If you rename all your properties or decide to store them into an array you should not need to adapt your tests at all. You want your tests to tell you that everything still works! When you need to change the tests to make sure everything still works you lose all the benefits as you also could make an error changing the tests.
So, all in all, you lose the value of you test suite!
Just testing the get/set combinations would be ok enough but usually not every setter should have a getter and just creating them for testing is not a nice thing ether.
Usually, you set some stuff and then tell the method to DO (behavior) something. Testing for that (that the class does what is should do) is the best option for testing and should make testing the properties superfluous.
If you really want to do that there is the setAccessible functionality in PHP reflections API but I can't make up an example where I find this desirable
Finding unused properties to catch bugs / issues like this one:
The PHP Mess Detector As a UnusedPrivateField Rule
class Something
{
private static $FOO = 2; // Unused
private $i = 5; // Unused
private $j = 6;
public function addOne()
{
return $this->j++;
}
}
This will generate two warnings for you because the variables are never accessed
I just want to point out one thing. Let's forget about private fields for a moment and focus on what client of your class cares about. Your class exposes a contract, in this case - ability to alter and retrieve name (via getter and setter). Expected functionality is simple:
when I set name with setName to "Gerald", I expect to get "Gerald" when I call getName
That's all. Client won't (well, shouldn't!) care about internal implementation. Whether you used private field name, hashset or called web service via dynamically generated code - doesn't matter for client. The bug you are currently experiencing, from user point of view - is not a bug at all.
Whether PHPUnit allows you to test private variables - I don't know. But from unit-testing perspective, you shouldn't do that.
Edit (in response to comment):
I understand your concerns about possible exposure of internal state, however I don't think unit testing is the right tool to deal with that. You can come up with a lot of possible scenarios how something might do something else which wasn't planned. Unit tests are by no means cure for all and shouldn't be used as such.
I agree with the others that in general you want to avoid accessing privates in your tests, but for the cases where you need to, you can use reflection to read and write the property.
I have problem when trying to mock a service in order to unit test it.
in my test class I have,
$mock = $this->getMock('MyClass');
$mock->expects($this->any())->method('method_2')->will($this->returnValue('fake_value'));
in my service, method_2() calls another method (let's say method_1()')
As I want to unit test only method_1() I need to mock only method_2()
but when I am running this test, method_1() already returns null.
Do you have any idea on why I'm already getting null?
Take a look at getMock() helper signature, it allows you to pass an array of methods as a second argument, those methods are then mocked and returns null (unless you define the valus each one of them should return)
In your case all the methods are mocked and return null except method_2() for which you forced the returned value.
Try again and replace,
$mock = $this->getMock('MyClass');
by,
$mock = $this->getMock('MyClass', array('method_2'));
I have this method that I would like to unit test. Since it's creating the Sql object inside the method, I can't mock it.
Initially I thought about making Sql be an instance property except that I'd have to reset it every time I use it in other methods and this will most likely lead to hard to debug errors (I don't want the possibility to get a "dirty" Sql object on other subsequent calls to its getter if at all avoidable).
What's the common pattern for testing these kinds of methods?
public function getConfigFromDb()
{
if (!is_null($this->configInDb)) {
return $this->configInDb;
}
$sql = new Sql($this->getSlaveDbAdapter());
$select = $sql->select()
->from('mytable');
$statement = $sql->prepareStatementForSqlObject($select);
$results = $statement->execute();
$results->buffer();
$this->configInDb = $return;
return $results;
}
#Julian you're right, I'll put it as an answer
In this case, I would add a SqlFactory and register it as a service (in factories param in service manager config), that way you can easily mock the service and the objects you'll get from it.
To go further on the subject, I delegate all object creation to factories that I can call using ServiceManager.
The fact is, that way, I can test my Factory in isolation, injecting all dependencies it needs, asserting that the actual object created is the expected object. And anytime I need an object from Factory in the method tested, I can serve a real instance, or a Mock.
Suppose I have a class with a private property and associated public getter and setter. I want to test with PHPUnit that the property gets the correct value after the setter has been used or that the getter returns the correct property.
Of course I can test the setter by using the getter to see that the object is storing the correct value, and vice versa for testing the getter. However, this doesn't guarantee that the private property is the one being set.
Say I had the following class. I created a property, getter and setter. But I made a typo in the property name, so the getter and the setter don't actually manipulate the property they're meant to manipulate
class SomeClass
{
private
$mane = NULL; // Was supposed to be $name but got fat-fingered!
public function getName ()
{
return ($this -> name);
}
public function setName ($newName)
{
$this -> name = $newName;
return ($this);
}
}
If I run the following test
public function testSetName ()
{
$this -> object -> setName ('Gerald');
$this -> assertTrue ($this -> object -> getName () == 'Gerald');
}
I would get a pass. However, something very bad has actually happened that I don't want. When setName() is called, it actually creates a new property in the class with the name I thought my private property had, only the one that the setter creates is public! I can demonstrate that with the following code:
$a = new SomeClass;
$a -> setName('gerald');
var_dump ($a -> getName ());
var_dump ($a -> name);
It would output:
string(6) "gerald"
string(6) "gerald"
Is there any way I can access the private properties from PHPUnit so I can write tests that make sure that the properties I think are being get and set actually really are being get and set?
Or is there some other thing I should be doing in a test to catch problems like this without trying to get access to the private state of the object under test?
You can also use Assert::assertAttributeEquals('value', 'propertyName', $object).
See https://github.com/sebastianbergmann/phpunit/blob/3.7/PHPUnit/Framework/Assert.php#L490
For testing properties, I'd make the same arguments I make then talking about testing private methods.
You usually don't want to do this.
It's about testing observable behavior.
If you rename all your properties or decide to store them into an array you should not need to adapt your tests at all. You want your tests to tell you that everything still works! When you need to change the tests to make sure everything still works you lose all the benefits as you also could make an error changing the tests.
So, all in all, you lose the value of you test suite!
Just testing the get/set combinations would be ok enough but usually not every setter should have a getter and just creating them for testing is not a nice thing ether.
Usually, you set some stuff and then tell the method to DO (behavior) something. Testing for that (that the class does what is should do) is the best option for testing and should make testing the properties superfluous.
If you really want to do that there is the setAccessible functionality in PHP reflections API but I can't make up an example where I find this desirable
Finding unused properties to catch bugs / issues like this one:
The PHP Mess Detector As a UnusedPrivateField Rule
class Something
{
private static $FOO = 2; // Unused
private $i = 5; // Unused
private $j = 6;
public function addOne()
{
return $this->j++;
}
}
This will generate two warnings for you because the variables are never accessed
I just want to point out one thing. Let's forget about private fields for a moment and focus on what client of your class cares about. Your class exposes a contract, in this case - ability to alter and retrieve name (via getter and setter). Expected functionality is simple:
when I set name with setName to "Gerald", I expect to get "Gerald" when I call getName
That's all. Client won't (well, shouldn't!) care about internal implementation. Whether you used private field name, hashset or called web service via dynamically generated code - doesn't matter for client. The bug you are currently experiencing, from user point of view - is not a bug at all.
Whether PHPUnit allows you to test private variables - I don't know. But from unit-testing perspective, you shouldn't do that.
Edit (in response to comment):
I understand your concerns about possible exposure of internal state, however I don't think unit testing is the right tool to deal with that. You can come up with a lot of possible scenarios how something might do something else which wasn't planned. Unit tests are by no means cure for all and shouldn't be used as such.
I agree with the others that in general you want to avoid accessing privates in your tests, but for the cases where you need to, you can use reflection to read and write the property.
I've got a mock object in a test. The real object, PageRepository, implements a magic method using __call(), so if you call $pageRepository->findOneByXXXX($value_of_field_XXXX), it will search the database for records matching that parameter.
Is there a way to mock that method?
The real method call would look something like this:
$homepage = $pageRepository->findOneBySlug('homepage');
The test would look like this:
$mockPageRepository->expects($this->any())
->method('findOneBySlug')
->will($this->returnValue(new Page()));
But it doesn't work -- PHPUnit doesn't spot the method call. The only way to get it to see the method is to define the method explicitly in PageRepository.
PHPUnit's getMock() takes a second argument, an array with the names of methods to be mocked.
If you include a method name in this array, the mock object will contain a method with that name, which expects() and friends will work on.
This applies even for methods that are not defined in the "real" class, so something like the following should do the trick:
$mockPageRepository = $this->getMock('PageRepository', array('findOneBySlug'));
Keep in mind that you'll have to explicitly include any other methods that also need to be mocked, since only the methods named in the array are defined for the mock object.
I found better way to do this. Still not perfect, but you don't have to specify all mocking class methods by hand, only magic methods:
$methods = get_class_methods(PageRepository::class);
$methods[] = 'findOneBySlug';
$pageRepositoryMock = $this->getMockBuilder(PageRepository::class)
->disableOriginalConstructor()
->disableOriginalClone()
->disableArgumentCloning()
->disallowMockingUnknownTypes()
->setMethods($methods)
->getMock();