How to test an abstract class? - php

I am confused in the way how to start testing an abstract class.
Should I test every method?
What should I test?
Should I not test abstract classes?
An example:
abstract class Command
{
private $params;
public function with(array $params = [])
{
$this->params = $params;
}
public function getParams()
{
return $this->params;
}
abstract public function run();
}
Should I test it like:
/** #test */
public function is_an_abstract_class()
{
$command = $this->getReflectionClass();
$this->assertTrue($command->isAbstract());
}
/** #test */
public function has_an_run_method()
{
$command = $this->getReflectionClass();
$method = $this->getReflectionMethod('run');
$this->assertTrue($command->hasMethod('run'));
$this->assertTrue($method->isAbstract());
$this->assertTrue($method->isPublic());
$this->assertEquals(0, $method->getNumberOfParameters());
}

Should I not test abstract classes?
Under most circumstances, this would be my choice.
Reason #1: the fact that some class inherits from an abstract class is an implementation detail, not a behavior. We don't want to couple our tests to implementation details.
Reason #2: I would expect the code in the abstract class to be covered by the tests that cover its descendants.
If you design were emerging "test first", then you already have coverage of this code, because the abstract class would be something that you would introduce to your design via refactoring a class that was already under test.

Related

Correct way to extend classes with Symfony autowiring

I'm wondering if this is the correct way to extend and use classes with Symfonies autowiring.
For example, I have a BaseClass that instantiates and auto wires the entity manager.
class BaseClass
{
protected $entityManager;
public function __construct(EntityManagerInterface $entityManager)
{
$this->entityManager = $entityManager;
}
protected function someMethodIWantToUse(Entity $something)
{
// Do something there
$this->entityManager->persist($something);
$this->entityManager->flush();
}
}
Then I have a subclass that extends the BaseClass and needs access that method. So I let it autowire again and pass it to the parent constructor.
class SubClass extends BaseClass
{
private $handler;
public function __construct(EntityManagerInterface $em, SomeHandler $handler)
{
parent::__construct($em);
$this->handler = $handler;
}
public function SubClassMethod()
{
// Get some data or do something
$entity = SomeEntityIGot();
$this->someMethodIWantToUse($entity);
}
}
Now I'm wondering if this is actually the correct way to do this or there's something I'm missing and the parent class should be able to autowire the entitymanager by itself?
To summarize the comments, yes your way is correct. Depending on your use case there are alternatives.
This are the ways you can go about it:
1. Extending Class and using Constructor Injection (what you do)
class BaseClass {
protected $some;
public function __construct(SomeInterface $some)
{
$this->some = $some;
}
}
class SubClass extends BaseClass {
private $other;
public function __construct(SomeInterface $some, OtherInterface $other)
{
parent::__construct($some);
$this->other = $other;
}
}
2. Setter Injection
class BaseClass {
protected $some;
public function __construct(SomeInterface $some)
{
$this->some = $some;
}
}
class SubClass extends BaseClass {
private $other;
public function setOther(OtherInterface $other)
{
$this->other = $other;
}
}
Now setOther won't automatically be called, you have to "manually" call it by either specifying a calls property in your services.yaml file, as described here: https://symfony.com/doc/current/service_container/calls.html. This would then look something like this:
// services.yaml
App\SubClass:
calls:
- [setOther, ['#other']]
Or
// services.yaml
app.sub_class:
class: App\SubClass
calls:
- [setOther, ['#other']]
assuming, an implementation of OtherInterface is available as #other in the service container.
A more elegant solution if you're using autowiring, simply add a #required annotation to the function as described here: https://symfony.com/doc/current/service_container/autowiring.html#autowiring-calls, which would look like this:
/**
* #required
*/
public function setOther(OtherInterface $other)
{
$this->other = $other;
}
3. Property Injection
class BaseClass {
protected $some;
public function __construct(SomeInterface $some)
{
$this->some = $some;
}
}
class SubClass extends BaseClass {
public $other;
}
As with the Setter Injection, you'll need to tell Symfony to populate this property, by specifying it in your services.yaml file like this:
// services.yaml
App\SubClass:
properties:
other: '#other'
Or
// services.yaml
app.sub_class:
class: App\SubClass
properties:
other: '#other'
assuming, an implementation of OtherInterface is available as #other in the service container.
Conclusion:
Since there are different ways to solve this, it's up to you to determine the correct way for your use case. I personally go with either option 1 (Constructor Injection) or option 2 (Setter Injection) using the annotation. Both of them allow you to use typehints and thus allowing your IDE to help you write clean code.
In 90% of cases, I'd go with option 1, as then it's clear for every one reading your code, what services are available with one glance at the __constructor function.
One use case for Setter Injection would be a base class offering all the setXXX functions but then sub classes not needing all of them. You could have a constructor in each sub class, requesting the needed services and then calling the setXXX methods of the base class.
Note: this is kind of an edge case and you probably won't run into this.
You can find a list of advantages and disadvantages of each method directly in the Symfony documentation about the Service Container -> Types of Injection
This way too :
class BaseClass
{
protected Environment $twig;
#[Required]
public function setTwig(Environment $twig): void
{ $this->twig = $twig; }
}
class ChildClass extends BaseClass
{
public function __construct(
private EntityManagerInterface $entityManager
) { }
public function test()
{
$this->twig->render(......);
}
}

PHPUnit testing singleton

I am trying to write a test for some very old code we have using PHPUnit. I've given the rough structure of it here, I am trying to test the isMember() method of ClassB which it inherits from ClassA. It should just be checking if a constant value exists in the class.
The problem I am having is that it is obviously a protected constructor, so I don't know how to test this as I keep getting protected contruct errors in PHPUnit as obviously the constructor is protected. Please advise how I test this?
abstract class ClassA implements InterfaceA {
private $mValueList;
protected static $instance;
protected function __construct() {
}
protected static function getInstance(ClassA $obj) {
if (is_null($obj->instance)) {
$obj->instance = $obj;
}
return $obj->instance;
}
public function isMember($value) {
return isset($this->mValueList[$value]);
}
....more methods......
}
class ClassB extends ClassA {
public static function getInstance() {
return parent::getInstance(new self());
}
const CON1 = 'string1';
const CON2 = 'string2';
}
You would test it like any other class, you just don't use the constructor in the test.
public function testIsMember() {
$classB = ClassB::getInstance();
$this->assertTrue($classB->isMember('string1'));
$this->assertFalse($classB->isMember('foo'));
}
I am making a guess on the assertions and you could refactor the test to use a data provider. But the general idea is the same. You don't invoke the constructor directly in your tests for a singleton. The getInstance method replaces the call to the constructor.

PHP abstract class does not affect the child of its child

I have an abstract class that declares the methods required to its children. It also has a construstor that its children inherits. How can I make the abstract class affect the children of the classes that extends it. To further clarify my question, here is my case:
The Abstract Class (abstract.php):
<?php
include_once 'database.php';
include_once 'validation.php';
abstract class DataOperations extends DatabaseConnection {
//The error string shared by all children of DataOperations
//This will be the message to be displayed in case validation failure
public $validator;
public $err_valid_string;
/**
* The DataOperations' constructor ensures that all of its children can perform database operation
* by automatically starting it for them. In case a child overrides this constructor, this child
* must explicitly start the connection to prevent fatal errors. Also, $validator must be re-instantiated
*/
public function __construct() {
$this->startDBConnection();
$this->validator = new InputValidator();
}
public function __destruct() {
}
abstract public function validateData();
abstract public function loadRecord($key, $cascade);
abstract public function saveRecord();
abstract public function updateRecord();
abstract public function deleteRecord();
}
?>
Now, here is the child object that extends the DataOperations abstract class
class Guest extends DataOperations {
//some properties here
public function validateData() {
//implementation
}
public function newRecord(implementation) {
//implementation
}
public function loadRecord($key, $cascade){
//implementation
}
public function saveRecord() {
//implementation
}
public function updateRecord() {
//implementation
}
public function deleteRecord() {
//implementation
}
}
?>
And here is another class, which is a child of Guest
class Booking extends Guest {
//some properties here
public function validateData() {
//implementation
}
public function newRecord(implementation) {
//implementation
}
public function loadRecord($key, $cascade){
//implementation
}
public function saveRecord() {
//implementation
}
public function updateRecord() {
//implementation
}
public function deleteRecord() {
//implementation
}
}
?>
The problem is, if I remove a method in Booking, say deleteRecord(), PHP won't throw an error because I think abstract class doesn't affect its 'grandchildren'. How can I fix this? I thought of using interfaces but my system already has 11 classes that depends to some methods of the abstract class. It will require intensive refactoring.
As you himself stated interface is best suited solution. Like
include_once 'database.php';
include_once 'validation.php';
interface DbInterface {
abstract public function validateData();
abstract public function loadRecord($key, $cascade);
abstract public function saveRecord();
abstract public function updateRecord();
abstract public function deleteRecord();
}
class DataOperations extends DatabaseConnection {
//The error string shared by all children of DataOperations
//This will be the message to be displayed in case validation failure
public $validator;
public $err_valid_string;
/**
* The DataOperations' constructor ensures that all of its children can perform database operation
* by automatically starting it for them. In case a child overrides this constructor, this child
* must explicitly start the connection to prevent fatal errors. Also, $validator must be re-instantiated
*/
public function __construct() {
$this->startDBConnection();
$this->validator = new InputValidator();
}
public function __destruct() {
}
}
class Guest extends DataOperations implements DbInterface {
- - -
}
class Booking extends Guest implements DbInterface {
- - -
}
First as you see I removed abstract from parent class as I assuming only those methods are abstract. Second as per your problem of 11 classes depend on Abstract class, I would say As you only remove abstract methods, Class implementing abstract methods now should implement interface. It is one time needed task. While classes using other normal methods of abstract class work like previous.
The best and cleanest way would be to have your "BOOKING" class extend the "DATAOPERATIONS" class, instead of GUEST, because looks like you don't have any extra methods in the BOOKING class. other wise make and interface and implement it. That is not the preferred way but you would have to give more info your situation.
To be clear, re-declaring a method in a child class will overwrite the parent class's implementation of that method when called from the child class, while not affecting any additional functionality provided by extending the parent class:
class a
{
function hello()
{
echo "Hello";
}
function goodbye()
{
echo "Goodbye";
}
}
/**
* class b overwrites class a's implementation of method goodbye but will retain
* it's definition for method hello
*/
class b extends a
{
function goodbye()
{
echo "See ya!";
}
}
$object = new b();
$object->hello(); // Hello
$object->goodbye();// See ya!
It appears that you want to implement a consistent interface across multiple class definitions. If this is the case, you will likely want to explore using PHP's interfaces.
These allow you to specify the methods that must exist in your class definition along with their set of arguments (collectively known as the signature). Your class definitions will implement an interface and if your definition does not meet the interface implementation specification, a fatal error will be thrown.
From the PHP manual:
// Declare the interface 'iTemplate'
interface iTemplate
{
public function setVariable($name, $var);
public function getHtml($template);
}
// Implement the interface
// This will work
class Template implements iTemplate
{
private $vars = array();
public function setVariable($name, $var)
{
$this->vars[$name] = $var;
}
public function getHtml($template)
{
foreach($this->vars as $name => $value) {
$template = str_replace('{' . $name . '}', $value, $template);
}
return $template;
}
// This will not work
// Fatal error: Class BadTemplate contains 1 abstract methods
// and must therefore be declared abstract (iTemplate::getHtml)
class BadTemplate implements iTemplate
{
private $vars = array();
public function setVariable($name, $var)
{
$this->vars[$name] = $var;
}
}
You can find more information about interface in the PHP manual:
http://us2.php.net/interface
Finally, it looks like you are hoping to define a common constructor for the child classes. Your child classes can both extend the DataOperations class while implementing a separate interface:
class Guest extends DataOperations implements DatabaseWriter
...

PHPUnit Stubbing Class methods declared as "final"

I'm writing a unit test for a class method that calls another class's method using a mock, only the method that needs to be called is declared as final, so PHPUnit is unable to mock it. Is there a different approach I can take?
example:
class to be mocked
class Class_To_Mock
{
final public function needsToBeCalled($options)
{
...
}
}
my test case
class MyTest extends PHPUnit_Framework_TestCase
{
public function testDoSomething()
{
$mock = $this->getMock('Class_To_Mock', array('needsToBeCalled'));
$mock->expects($this->once())
->method('needsToBeCalled')
->with($this->equalTo(array('option'));
}
}
Edit: If using the solution provided by Mike B and you have a setter/getter for the object you're mocking that does type checking (to ensure the correct object was passed into the setter), you'll need to mock the getter on the class you're testing and have it return the other mock.
example:
class to be mocked
class Class_To_Mock
{
final public function needsToBeCalled($options)
{
...
}
}
mock
class Class_To_MockMock
{
public function needsToBeCalled($options)
{
...
}
}
class to be tested
class Class_To_Be_Tested
{
public function setClassToMock(Class_To_Mock $classToMock)
{
...
}
public function getClassToMock()
{
...
}
public function doSomething()
{
$this->getClassToMock()
->needsToBeCalled(array('option'));
}
}
my test case
class MyTest extends PHPUnit_Framework_TestCase
{
public function testDoSomething()
{
$classToTest = $this->getMock('Class_To_Be_Tested', array('getClassToMock'));
$mock = $this->getMock('Class_To_MockMock', array('needsToBeCalled'));
$classToTest->expects($this->any())
->method('getClassToMock')
->will($this->returnValue($mock));
$mock->expects($this->once())
->method('needsToBeCalled')
->with($this->equalTo(array('option'));
$classToTest->doSomething();
}
}
I don't think PHPUnit supports stubbing/mocking of final methods. You may have to create your own stub for this situation and do some extension trickery:
class myTestClassMock {
public function needsToBeCalled() {
$foo = new Class_To_Mock();
$result = $foo->needsToBeCalled();
return array('option');
}
}
Found this in the PHPUnit Manual under Chapter 11. Test Doubles
Limitations
Please note that final, private and static methods cannot be stubbed or mocked. They are ignored by PHPUnit's test double functionality and retain their original behavior.
I just stumbled upon this issue today. Another alternative is to mock the interface that the class implements, given that it implements an interface and you use the interface as type hinting.
For example, given the problem in question, you can create an interface and use it as follows:
interface Interface_To_Mock
{
function needsToBeCalled($options);
}
class Class_To_Mock implements Interface_To_Mock
{
final public function needsToBeCalled($options)
{
...
}
}
class Class_To_Be_Tested
{
public function setClassToMock(Interface_To_Mock $classToMock)
{
...
}
...
}
class MyTest extends PHPUnit_Framework_TestCase
{
public function testDoSomething()
{
$mock = $this->getMock('Interface_To_Mock', array('needsToBeCalled'));
...
}
}

Best practices to test protected methods with PHPUnit

I found the discussion on Do you test private method informative.
I have decided, that in some classes, I want to have protected methods, but test them.
Some of these methods are static and short. Because most of the public methods make use of them, I will probably be able to safely remove the tests later. But for starting with a TDD approach and avoid debugging, I really want to test them.
I thought of the following:
Method Object as adviced in an answer seems to be overkill for this.
Start with public methods and when code coverage is given by higher level tests, turn them protected and remove the tests.
Inherit a class with a testable interface making protected methods public
Which is best practice? Is there anything else?
It seems, that JUnit automatically changes protected methods to be public, but I did not have a deeper look at it. PHP does not allow this via reflection.
If you're using PHP5 (>= 5.3.2) with PHPUnit, you can test your private and protected methods by using reflection to set them to be public prior to running your tests:
protected static function getMethod($name) {
$class = new ReflectionClass('MyClass');
$method = $class->getMethod($name);
$method->setAccessible(true);
return $method;
}
public function testFoo() {
$foo = self::getMethod('foo');
$obj = new MyClass();
$foo->invokeArgs($obj, array(...));
...
}
teastburn has the right approach. Even simpler is to call the method directly and return the answer:
class PHPUnitUtil
{
public static function callMethod($obj, $name, array $args) {
$class = new \ReflectionClass($obj);
$method = $class->getMethod($name);
$method->setAccessible(true);
return $method->invokeArgs($obj, $args);
}
}
You can call this simply in your tests by:
$returnVal = PHPUnitUtil::callMethod(
$this->object,
'_nameOfProtectedMethod',
array($arg1, $arg2)
);
You seem to be aware already, but I'll just restate it anyway; It's a bad sign, if you need to test protected methods. The aim of a unit test, is to test the interface of a class, and protected methods are implementation details. That said, there are cases where it makes sense. If you use inheritance, you can see a superclass as providing an interface for the subclass. So here, you would have to test the protected method (But never a private one). The solution to this, is to create a subclass for testing purpose, and use this to expose the methods. Eg.:
class Foo {
protected function stuff() {
// secret stuff, you want to test
}
}
class SubFoo extends Foo {
public function exposedStuff() {
return $this->stuff();
}
}
Note that you can always replace inheritance with composition. When testing code, it's usually a lot easier to deal with code that uses this pattern, so you may want to consider that option.
I'd like to propose a slight variation to getMethod() defined in uckelman's answer.
This version changes getMethod() by removing hard-coded values and simplifying usage a little. I recommend adding it to your PHPUnitUtil class as in the example below or to your PHPUnit_Framework_TestCase-extending class (or, I suppose, globally to your PHPUnitUtil file).
Since MyClass is being instantiated anyways and ReflectionClass can take a string or an object...
class PHPUnitUtil {
/**
* Get a private or protected method for testing/documentation purposes.
* How to use for MyClass->foo():
* $cls = new MyClass();
* $foo = PHPUnitUtil::getPrivateMethod($cls, 'foo');
* $foo->invoke($cls, $...);
* #param object $obj The instantiated instance of your class
* #param string $name The name of your private/protected method
* #return ReflectionMethod The method you asked for
*/
public static function getPrivateMethod($obj, $name) {
$class = new ReflectionClass($obj);
$method = $class->getMethod($name);
$method->setAccessible(true);
return $method;
}
// ... some other functions
}
I also created an alias function getProtectedMethod() to be explicit what is expected, but that one's up to you.
I think troelskn is close. I would do this instead:
class ClassToTest
{
protected function testThisMethod()
{
// Implement stuff here
}
}
Then, implement something like this:
class TestClassToTest extends ClassToTest
{
public function testThisMethod()
{
return parent::testThisMethod();
}
}
You then run your tests against TestClassToTest.
It should be possible to automatically generate such extension classes by parsing the code. I wouldn't be surprised if PHPUnit already offers such a mechanism (though I haven't checked).
I'm going to throw my hat into the ring here:
I've used the __call hack with mixed degrees of success.
The alternative I came up with was to use the Visitor pattern:
1: generate a stdClass or custom class (to enforce type)
2: prime that with the required method and arguments
3: ensure that your SUT has an acceptVisitor method which will execute the method with the arguments specified in the visiting class
4: inject it into the class you wish to test
5: SUT injects the result of operation into the visitor
6: apply your test conditions to the Visitor's result attribute
You can indeed use __call() in a generic fashion to access protected methods. To be able to test this class
class Example {
protected function getMessage() {
return 'hello';
}
}
you create a subclass in ExampleTest.php:
class ExampleExposed extends Example {
public function __call($method, array $args = array()) {
if (!method_exists($this, $method))
throw new BadMethodCallException("method '$method' does not exist");
return call_user_func_array(array($this, $method), $args);
}
}
Note that the __call() method does not reference the class in any way so you can copy the above for each class with protected methods you want to test and just change the class declaration. You may be able to place this function in a common base class, but I haven't tried it.
Now the test case itself only differs in where you construct the object to be tested, swapping in ExampleExposed for Example.
class ExampleTest extends PHPUnit_Framework_TestCase {
function testGetMessage() {
$fixture = new ExampleExposed();
self::assertEquals('hello', $fixture->getMessage());
}
}
I believe PHP 5.3 allows you to use reflection to change the accessibility of methods directly, but I assume you'd have to do so for each method individually.
I suggest following workaround for "Henrik Paul"'s workaround/idea :)
You know names of private methods of your class. For example they are like _add(), _edit(), _delete() etc.
Hence when you want to test it from aspect of unit-testing, just call private methods by prefixing and/or suffixing some common word (for example _addPhpunit) so that when __call() method is called (since method _addPhpunit() doesn't exist) of owner class, you just put necessary code in __call() method to remove prefixed/suffixed word/s (Phpunit) and then to call that deduced private method from there. This is another good use of magic methods.
Try it out.
Alternative.The code below is provided as an example.
Its implementation can be much broader.
Its implementation that will help you test private methods and replacing a private property .
<?php
class Helper{
public static function sandbox(\Closure $call,$target,?string $slaveClass=null,...$args)
{
$slaveClass=!empty($slaveClass)?$slaveClass:(is_string($target)?$target:get_class($target));
$target=!is_string($target)?$target:null;
$call=$call->bindTo($target,$slaveClass);
return $call(...$args);
}
}
class A{
private $prop='bay';
public function get()
{
return $this->prop;
}
}
class B extends A{}
$b=new B;
$priv_prop=Helper::sandbox(function(...$args){
return $this->prop;
},$b,A::class);
var_dump($priv_prop);// bay
Helper::sandbox(function(...$args){
$this->prop=$args[0];
},$b,A::class,'hello');
var_dump($b->get());// hello
You can use Closure as in the code below
<?php
class A
{
private string $value = 'Kolobol';
private string $otherPrivateValue = 'I\'m very private, like a some kind of password!';
public function setValue(string $value): void
{
$this->value = $value;
}
private function getValue(): string
{
return $this->value . ': ' . $this->getVeryPrivate();
}
private function getVeryPrivate()
{
return $this->otherPrivateValue;
}
}
$getPrivateProperty = function &(string $propName) {
return $this->$propName;
};
$getPrivateMethod = function (string $methodName) {
return Closure::fromCallable([$this, $methodName]);
};
$objA = new A;
$getPrivateProperty = Closure::bind($getPrivateProperty, $objA, $objA);
$getPrivateMethod = Closure::bind($getPrivateMethod, $objA, $objA);
$privateByLink = &$getPrivateProperty('value');
$privateMethod = $getPrivateMethod('getValue');
echo $privateByLink, PHP_EOL; // Kolobok
$objA->setValue('Zmey-Gorynich');
echo $privateByLink, PHP_EOL; // Zmey-Gorynich
$privateByLink = 'Alyonushka';
echo $privateMethod(); // Alyonushka: I'm very private, like a some kind of password!
I made a class for invoking easily private methods (static and non-static) for unit-testing purposes:
class MethodInvoker
{
public function invoke($object, string $methodName, array $args=[]) {
$privateMethod = $this->getMethod(get_class($object), $methodName);
return $privateMethod->invokeArgs($object, $args);
}
private function getMethod(string $className, string $methodName) {
$class = new \ReflectionClass($className);
$method = $class->getMethod($methodName);
$method->setAccessible(true);
return $method;
}
}
Example of usage:
class TestClass {
private function privateMethod(string $txt) {
print_r('invoked privateMethod: ' . $txt);
}
}
(new MethodInvoker)->invoke(new TestClass, 'privateMethod', ['argument_1']);

Categories