How can I set protected properties of mocks for abstract classes? - php

I've looked around and I found a solution that works for normal objects, but it doesn't seem to work for mocks.
The test below fails with the message: Unable to set property someProperty of object type Mock_ClassToTest_40ea0b83: Property someProperty does not exist.
class sampleTestClass extends PHPUnit_Framework_TestCase
{
function test() {
$object = $this->getMockForAbstractClass(ClassToTest::class, [], '', false);
$this->setProtectedProperty($object, 'someProperty', 'value');
}
private function getReflectionProperty($object, $property) {
$reflection = new ReflectionClass($object);
$reflectionProperty = $reflection->getProperty($property);
$reflectionProperty->setAccessible(true);
return $reflectionProperty;
}
/**
* This method modifies the protected properties of any object.
* #param object $object The object to modify.
* #param string $property The name of the property to modify.
* #param mixed $value The value to set.
* #throws TestingException
*/
function setProtectedProperty(&$object, $property, $value) {
try {
$reflectionProperty = $this->getReflectionProperty($object, $property);
$reflectionProperty->setValue($object, $value);
}
catch ( Exception $e ) {
throw new TestingException("Unable to set property {$property} of object type " . get_class($object) .
': ' . $e->getMessage(), 0, $e);
}
}
}
abstract class ClassToTest
{
private $someProperty;
abstract function someFunc();
}
class TestingException extends Exception
{
}
EDIT: 8/31/2016 4:32 PM EST
Updated code in response to answer by Katie.

You are trying to call Reflection methods on a mocked object, instead, you can call it on the abstract class itself:
So change:
$reflection = new ReflectionClass(get_class($object));
to
$reflection = new ReflectionClass(ClassToTest::class);
And that will work for anything that is not abstract in the class, such as your property, or another method that is fully implemented.
Additional Note since OP was updated
The fix will still work for your first line in the getReflectionProperty. But if you don't have access to the class name, then that is a problem.

Using reflection to access protected and private properties and methods of classes in tests seems a very clever method but it leads to tests that are difficult to read and understand.
On the other hand, only the public interface of a class should be tested. Testing (and even caring about) the protected and private properties and methods of the tested class is a sign that the tests are written after the code. Such tests are brittle; any change in the implementation of the tested class breaks the tests, even when it doesn't break the functionality of the class.
There is usually no need to test an abstract class. Most of the times the tests of its children classes cover the relevant code of the abstract class too. If they doesn't cover some part of it then either that code is not needed there or the test cases do not cover all the corner cases.
However, sometimes one needs to write a testcase for an abstract class. The best approach, in my opinion, is to extend the abstract class at the bottom of the file that contains the test case, provide simple implementations for all its abstract methods and use this class as SUT.
Something along these lines:
class sampleTestClass extends PHPUnit_Framework_TestCase
{
public function testSomething()
{
$object = new ConcreteImplementation();
$result = $object->method1();
self::assertTrue($result);
}
}
class ConcreteImplementation extends AbstractClassToTest
{
public function someFunc()
{
// provide the minimum implementation that makes it work
}
}
You are testing a mock in the code you posted. The mocks are not meant to be tested. Their purpose is to simulate the behaviour of the collaborators of the SUT that are not appropriate to be instantiated in a test.
The reasons why a collaborator class is mocked in a test include, but are not limited to:
difficult creation; for example, when the constructor of the mocked class requires many arguments or other objects;
the collaborator is an abstract class or an interface; the actual implementation might not even exist when the test and the tested class are written;
the code of the collaborator takes a lot of time to complete or requires additional resources (disk space, database connection, Internet connection etc);
the code of the collaborator has permanent side effects; this is usually coupled with the previous reason.

Related

Testing setting a private property: can we use the mock to set the value and reflect the property to check if value was set?

I have both a "way of doing this" and "how is it done" question(s).
Class realClass {
private $privateProperty;
public function __construct($someArray, $someString) {
// stuff
}
public function setProperty($someArray) {
$this->privateProperty = $someArray;
}
}
class testRealClass extends TestCase {
// creating a mock because don't want to have tests depending on arguments passed to the constructor
public function setUp(): void
{
$this->classToTest = $this->getMockBuilder(realClass::class)
->disableOriginalConstructor()
->setMethods(null)
->getMock();
}
// testing the set of the variable. In order to know if it was set, without using a getter,
// we probably need to use reflection
public function testSetPrivateProperty() {
$this->classToTest->setProperty([1,2,3]);
// This will not work as the property is private
// echo $this->classToTest->privateProperty;
// Reflecting the mock, makes sense? How does this work?
$reflect = new \ReflectionClass($this->classToTest);
$property = $reflect->getProperty('privateProperty');
$property->setAccessible(true);
var_dump($property->getValue($this->classToTest));
}
}
When executing this I get a ReflectionException: Property privateProperty does not exist.
If I do a print_r of the reflections->getProperties I can see the property there.
I understand this would be easily achievable with just a getter in the realClass but take this as an exercise.
Doest this make sense at all or this will not work because it is just wrong?
Thank you everyone
(Wrote the code here so if I'm missing ";", "(", etc, do ignore it :)
Using Reflection API for testing is seldom a good idea. However, the real question is - why have a private property if nothing can access it? Obviously, the property is used in another method of the class. So, the clean solution here is remove all reflection code, then create a getter in the class itself, and replace all calls to $this->property to $this->getProperty(), then you can easily test this.
For a more generalized take on this question, private properties and methods are not meant to be tested, they are private because they conceal their data from everyone, including the tester classes. The more private fields and properties your class has, the less testable it gets, so when this becomes a hindrance, it is better to split the class into two, making one of the private methods public.

How to test correct call of constructor with PHPUnit

Using PHPUnit, how can I properly test that the constructor (for class Built in the simplified example below) is being correctly called? Class Container is the class being tested. I want to be able to catch things like the intentional typo in the array key, below. It's not possible to mock constructors, otherwise this would be straight forward.
class Container {
public function create(string $input) {
$request = new Built(["rid" => $input]); // Oops, typo in keyname.
}
}
class Built {
private $tid;
public function __construct(array $params) {
$this->tid = $params["tid"];
}
}
This is a different situation from that described in How to unit test the methods of a class whose constructor take some arguments?, because my class under test is instantiating the second class.
A constructor should not do any work other than 1) verify data passed to it and abort object construction with an exception when the data is rejected and 2) assign data passed to it to properties. Therefore the only thing that can and should be tested with regards to object construction is that invalid data is rejected and does not lead to the construction of an object that violates the contract of the class.

Enforcing variable declaration for a subclass in php

I have an abstract class like this
<?php
abstract class AbastractCreationCommand extends AbstactCommand {
protected $repository;
function handle($payload) {
$this->repository->create($payload);
}
}
class TagCreationCmd extends AbstractCreationCommand {
function __constructor() {
$this->repository = new TagRepository();
}
}
?>
Questions:
is there a way I could enforce the definition of the repository class in the subclasseses of the AbstractCreationCommand ?
Do I need to create a test for each subclass and call handle method or is another way to test all my code?
Answering number 1: You cannot enforce the creation of anything in subclasses from within your abstract class. At least not during instantiation. Everything in the subclass is optional.
However, your code in the abstract class could check whether or not the necessary objects have been defined when executing the part of the code that needs it, like this:
abstract class AbastractCreationCommand extends AbstactCommand {
protected $repository;
function handle($payload) {
if (!$this->repository instanceof TagRepository) {
throw new \InvalidArgumentException('Need a TagRepository');
}
$this->repository->create($payload);
}
}
However, is likely doing the complaining too late. And the reason may be because you are using inheritance instead of composition, or are inheriting the wrong things.
First of all, you are not doing dependency injection. Your subclass should not directly instantiate that TagRepository. This leads to problems testing your abstract class' code, as well as the subclass code, because you cannot provide a mock object instead. This severely limits the ability to test your code in isolation.
Also, the subclass cannot work without knowing very specifically how to inherit the abstract class beyond implementing any abstract functions. If both abstract and subclass come from you as the author, I would consider it to be ok to impose doing all things correctly on you. But if you expect other developers to inherit that abstract class (and your question sounds like this might be the background problem), then you shouldn't do this at all.
Abstract classes do provide some common functions to a set of subclasses through inheritance. But the same thing could be achieved if you put all the code into a non-abstract class and inject this class into independent ex-sub classes. They will call these common functions as public methods instead of private or protected, and the testing of the common code is also easier, because the methods are public.
Also note that you already have three levels of inheritance, which is nearing a uncomfortable level: AbstractCommand -> AbstractCreationCommand -> TagCreationCmd.
The problem is that everything you change in AbstractCommand has to be done with two levels of inheriting objects in mind. You cannot simply change a protected variable's name. You cannot simply add a protected (or public) variable without checking if any of the sub classes already has such a variable with the same name - unless you intend to share it.
The problems with maintaining code that is inherited is not with the classes at the end of the inheritance chain, but with these at the top. Just think about how many classes might be affected with different usage contexts: If you have AbstractCreationCommands, you will have AbstractDeletionCommands and AbstractChangeCommands and AbstractDoNothingCommands, and a plethora of concrete commands of all these kinds doing plenty of different stuff. Just imaging that on each level, you have four classes - this makes you having to maintain one base class, four inheriting classes, and four times four concrete classes - for a whopping 21 classes in total, all of them having to be tested, and likely none of them gaining any benefit from being an instanceof AbstractCommand.
Answering number 2: Yes, you have to test all subclasses - these are the ones that get instantiated and used. You should also test the abstract class' code in isolation. PHPUnit offers to instantiate an abstract class with the mock framework, so any abstract method would be mocked and could be configured. However, I always have a bad feeling when I use a mock as the real tested object because I am not really testing the pure code, but some combination of mock code and real code.
A possible way out is to create a test class that barely does anything beyond extending the abstract class, and work with this one.
1.Yes. I do the same when I need to force instantiation of some dependancies. And this way is support GRASP: Creator principle.
Another way to accomplish that with dependency injection, but this way broke Creator principle:
class TagCreationCmd extends AbstractCreationCommand {
function __constructor(TagRepositoryInterface $tagRepository) {
$this->repository = $tagRepository;
}
}
If follow 3 rules of TDD you should write test for each line of code. So the answer is yes.
Is there a way I could enforce the definition of the repository class in the subclasseses of the AbstractCreationCommand ?
I don't see the necessity tbh. If your AbstractCreationCommand needs a repo to work, add it as a constructor param. This doesn't enforce the repo to be injected because a subtype can override the constructor, but it should be abundantly clear that an AbstractCreationCommand subtype requires some sort of repo then, e.g.
abstract class AbstractCreationCommand extends AbstractCommand
{
private $repository;
public function __construct(Repository $repository)
{
$this->repository = $repository
}
protected function getRepository(): Repository
{
return $this->repository;
}
// …
You could also use a Template Method pattern to indicate that any subtype will utilize a repo by adding an abstract getter for the repo. The subtype will have to implement that method then. It's then up to the developer to decide on the implementation:
abstract class AbstractCreationCommand extends AbstractCommand
{
public function handle()
{
$this->getRepository()->create();
}
abstract function getRepository(): Repository;
// …
If you really must enforce it at creation level, you can set the abstract type's constructor to final protected and do any subtype creation in a static factory method, e.g.
abstract class AbstractCreationCommand extends AbstractCommand
{
private $repository;
final protected function __construct(Repository $repository)
{
$this->repository = $repository;
}
// …
This now prevents direct instantiation of any subtypes via new. Trying to new a subtype, will result in PHP Fatal error.
Instead the subtype must be created like this:
class TagCreationCommand extends AbstractCreationCommand
{
private $foo;
public static function create(Repository $repository, Foo $foo)
{
$command = new static ($repository);
$command->setFoo($foo);
return $command;
}
protected function setFoo(Foo $foo)
{
$this->foo = $foo;
}
// …
Then you'd call TagCreationCommand::create(new TagRepository, new Foo); to get a new instance. Since you cannot override the constructor and have to call the parent constructor from within the static create method, you effectively enforce a Repository now to be there. I added the Foo stuff only to illustrate how you'd use additional dependencies.
As you can hopefully see, this requires quite a lot of gymnastics compared to the much more lightweight previous two approaches that will basically result in the same outcome. After all, if there is no repo, the code will fail. And since you are using tests, this will get noticed. So why bother?
Do I need to create a test for each subclass and call handle method or is another way to test all my code?
If you are overriding the handle method, you should test that behavior in a concrete test class for that subtype.
If your subtypes do not override the handle method, you can create an AbstractCreationCommandTest and put a test for the handle method in there. However, if that is the case, I wonder why you need the AbstractCreationCommand to be abstract in the first place because then it sounds like you just need a CreationCommand.
Following Yan Burtovoy's suggestion, I would go even further and actually enforce a DI container
<?php
abstract class AbastractCreationCommand extends AbstactCommand {
protected $repository;
function __constructor(\DI\Container $container) {
$this->repository = $container->get('TagRepository');
}
function handle($payload) {
$this->repository->create($payload);
}
}
You should create tests for everything that is exposed to users of your library (that would be your application).
So, if you have a subclass that relies on handle() being called then you should write a test for that. Reason being that in 6 months someone might change the inheritance or overwrite the handle() method and change the initial expected behaviour.

PHPUnit mock class that has named static constructor

Given I have a FruitSalad class (the system under test):
class FruitSalad
{
protected $fruits = [];
public function addFruit(Fruit $fruit)
{
$this->fruits[] = $fruit;
return $this;
}
}
And I have a Fruit class:
class Fruit
{
public static function withName($name)
{
$instance = new MyDependencyClass();
$instance->name = $name;
return $instance;
}
}
A trivial example, however you can see that the Fruit class uses a named static constructor, and the addFruit() method on the FruitSalad class type hints Fruit as its expected parameter.
When writing a test for addFruit(), I need to mock the Fruit class.
function test_it_can_add_a_fruit_to_its_list_of_fruits()
{
$fruit = $this->getMockBuilder('Fruit')
->disableOriginalConstructor()
->getMock();
$this->fruitSalad->addFruit($fruit);
// Do some assertion.
}
This creates a simple mock of the Fruit class, but I want to instantiate it via the withName() static method - and I do not want to expose a setter for the name property.
How can I create a mock for Fruit using the static named constructor?
PHPUnit used to support mocking static methods, but since PHPUnit 4.0 it's omitted. I see four options here:
1. Don't mock the method at all
You could just call the method and use it's logic, although you'd test the static method as well if you do and normally that's something you should avoid when writing unit tests.
2. Change the class
Ask yourself if this method really needs to be static and if not, change the class to test it properly. There are quite some use cases where it's better to change some of your architecture in order to write proper tests.
3. Use a spy class
Spy classes are classes that extend a class that you would usually mock, but implement some logic for testing the configuration of a class or the dependency of a tested method to another method. In the very most cases this can be avoided by mocking the class properly. Spies are simply your work around if mocks are not enough, there are very few cases in which you really need them.
However, in this case a spy could be used to overwrite a static method as a work around:
class FruitSpy extends Fruit
{
public static $return;
public static $name;
public static function withName($name) {
$expected = self::$name;
if($name == $expected) {
return self::$return;
} else {
throw new \RuntimeException("FruitSpy::withName(): Parameter 0 was $name, $expected expected");
}
}
}
This example checks for the correct $name and, if it's correct, returns your defined return. You'd use it like this in your test:
$fruitSpy = new FruitSpy();
$fruitSpy::$name = "Banana";
$fruitSpy::$return = new \stdClass();
$this->fruitSalad->addFruit($fruitSpy);
Not exactly a clean solution, but the only way I see if you absolutely positively don't want to change other code than the test code.
Again, you should think about changing the static method to a casual method if you need to do something like this.
4. Use PHPUni 3.*
You could simple use a deprecated version of PHPUnit to use this method. Not a preferred way either.
Conclusion
I don't see a clean way to mock a static method and ::staticExpects() was removed for a reason in 4.0
How can I create a mock for Fruit using the static named constructor?
You can't. Mocks are created by using a mocking framework.
Anyway it does not matter how mocks are created but, instead, how they behave, because they're external to the class being tested.
Just configure the mock so that it behaves the same way a real Fruit instance would when created using Fruit::withName.

Changing the visibility scope of parent methods in child classes

I've got a Validator class and a UserValidator class which extends from it.
My Validator has a public method setRule(...) with public visibility.
When I extend from it I want to change the visibility of the setRule(...) parent method to private/protected within the child so that it's only visible for the child and no outsiders can call this method from the child.
Is that possible? If so, how could I achieve it?
From an architectural point of view this is not recommended. As already stated in the comments the clean way would be to set your method to protected so only children can access it.
I cannot think of a single use case that would put me in the need to call a public method on a parent class but where I am not allowed to call it on the child class.
That's against the Open/Closed principle. Classes should be open for extension, but not for modification.
Since that was not the question I'll provide a way how that can be achieved though. But note:
This method makes use of an extra class which will be responsible for the instantiation
It's a hack. This solution will not make use of PHP's native language features when throwing accessibility errors.
First let's define the classes you already had
<?php
class Validator {
public function setRule()
{
echo "Hello World";
}
}
class UserValidator extends Validator {
public $prop = 'PROPERTY';
}
There's nothing special here. So let's go on and create a custom exception class for the visibility error.
<?php
class MethodNotAccessibleException extends Exception {}
This exception will be thrown when we try to invoke a "pseudo-private" method on the child class.
Now we want to create the Class that will be responsible for instantiating your child class. It is basically just a wrapper that defines a lock property which holds method names that should not be accessible.
<?php
class PrivateInstanceCreator {
protected $reflectionClass;
protected $lock = [];
protected $instance;
public function __construct($classname, $args = [])
{
// We'll store an instance of the reflection class
// and an instance of the real class
$this->reflectionClass = new ReflectionClass($classname);
$this->instance = $this->reflectionClass->newInstanceArgs($args);
return $this;
}
// The lock method is able to make a method on the
// target class "pseudo-private"
public function lock($method)
{
$this->lock[] = $method;
return $this;
}
// Some real magic is going on here
// Remember. This class is a wrapper for the real class
// if a method is invoked we look for the method
// in the real instance and invoke it...
public function __call($method, $args)
{
// ... but as soon as this method is defined as
// locked, we'll raise an exception that the method
// is private
if(in_array($method, $this->lock))
{
$reflectionMethod = $this->reflectionClass->getMethod($method);
if($reflectionMethod->isPublic())
throw new MethodNotAccessibleException('Method: __' . $method . '__ is private and could not be invoked');
}
return call_user_func_array([$this->instance, $method], $args);
}
// The same goes for properties
// But in this case we'll do no protection
public function __get($prop)
{
return $this->instance->{$prop};
}
}
Our final step is the instantiation.
<?php
$userValidator = new PrivateInstanceCreator('UserValidator', []);
$userValidator->lock('setRule');
$userValidator->setRule(); //Will throw an exception
Instead of instantiating the class directly we'll do it by using our custom wrapper class.
Of course you could handle it in the child class itself, but that's a way to accomplish your task without touching the classes directly.
Having said that, it is still a dirty hack whose usage should be avoided if possible. If you would instantiate the child class directly the inherited methods would still be public.
So if a developer has no knowlege about the wrapper class he'll have a hard time to figure out how to instantiate the child class properly.
Update:
To make the child class uninstantiable directly you can set the constructor to private and call newInstanceWithoutConstructor() from the reflection class, which is even dirtier, since that would make Dependency Injection for the class completely impossible. I'm just mentioning it for completenesses sake. Usage is still not recommended

Categories