Unit Test for Parent Method Call - php

Weirdly, not only have I failed to find an answer for this, but also this question doesn't seem to be a concern at all. It is for me, however.
How do you set an expectation of a parent method call with PHPUnit?
I already read the answers and discussions, the point of which is that "you don't need to test for that, you only test that it works". This is entirely wrong. Unit tests are there to test for the internals of the implementation. Testing that "it works" is for functional tests.
More context: I have an exception implementation, which accepts additional arguments to the constructor, and therefore naturally needs to call the parent::__construct() method, forwarding the message, the code, and the inner exception to it. I know that the parent method works, and I don't need to test it: it's a native PHP class, and even if it wasn't, that code is not in the class I am testing. So, all I need is to set some expectations, one of which is that the parent __construct() method is called with the appropriate args. Of course, the constructor is not the only case where this could be necessary.
Any ideas?

As for the PHPUnit and checking for parent methods calls it is unfortunately impossible. You can always create a mock of a class and check if the parent constructor was called but then you are only testing a mock and not your actual class. And this is definitely not what you want. As someone else already pointed out in another answer: you don't mock or stub subject-under-test (https://stackoverflow.com/a/6711948/4737924).
You are saying that you need to know is that the parent constructor was called. I think it is only a technical detail of the implementation. What you really need to know is if your exception behaves as it needs to behave.
Take a look at the following example:
class MyException extends Exception
{
private $somethingElse;
public function __construct(string $message, int $code, Throwable $previousException, SomethingElse $somethingElse)
{
$message = $message . ' - my additional message';
parent::__construct($message, $code, $previousException);
$this->somethingElse = $somethingElse;
}
public function getSomethingElse(): SomethingElse
{
return $this->somethingElse;
}
}
class MyExceptionTest extends TestCase
{
public function testIsCreatedProperly(): void
{
$previousException = new Exception();
$somethingElse = new SomethingElse();
$exception = new MyException('My message', 100, $previousException, $somethingElse);
$this->assertSame('My message - my additional message', $exception->getMessage());
$this->assertSame(100, $exception->getCode());
$this->assertSame($previousException, $exception->getPrevious());
$this->assertSame($somethingElse, $exception->getSomethingElse());
}
}
Here only the behavior matters. It is really irrelevant how it happened that the message is correct, or the code or the previous exception. All it matters is if it behaves as you wanted it to behave. You don't need to explicitly check if the parent constructor was called. You know it was called because the output is as expected.

You can create smth like the following trait, and use it in all classes, where you need to mock parent method call:
trait ParentCallTestable
{
/**
* Call parent methdod
* #param string $method Method name to call
* #return mixed
*/
public function parent($method, ...$args)
{
return parent::$method(...$args);
}
}
Then in a class, that uses this trait, standard parent call
$result = parent::fooMethod($arg1, $arg2);
is replaced with
$result = $this->parent('fooMethod', $arg1, $arg2);
and you can mock it easily.

Related

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.

Setting value for inner method in php unit

I can not set value for inner method when I try to test. Here I have written a sample class. I have created mock object for same class but does not effect.
class A
{
public function OneTest()
{
if($this->TwoTest()){
return true;
}
}
public function TwoTest()
{
// return somethings
}
}
I am new at phpunit test writing. if some one expert help me that good for me. I want to test this method. I have tried with:
class ATest extends \PHPUnit_Framework_TestCase
{
public function testOne()
{
$helper = new testOne();
// trying to set TwoTest() method value but does not effect.
$mock = $this->createMock(A::class);
$mock->method("TwoTest")
->willReturn(true);
$this->assertTrue($helper->OneTest();
}
}
Actually I do not know how to use my mocking method result. My actual implementation in twoTest method contains some db related code. I do not want to run db code in testing time.
You are pretty close with your mock. What you want to do is called partial mocking. This is done by creating a mock of A with only TwoTest being mocked, i.e. it will now always return true and never actually call the real code inside the original implementation in A, whereas all other methods still act as before. Therefore calling $mock->OneTest() should return the expected result. Since you make both calls on the (partially) mocked instance, you won't need $helper. So your test would probably look something like this:
public function testOneWhenTwoTestReturnsTrue()
{
$mock = $this->getMockBuilder(A::class)
->setMethods(["TwoTest"])
->getMock();
$mock->method("TwoTest")
->willReturn(true);
$this->assertTrue($mock->OneTest();
}
Notice that I use getMockBuilder() instead of just createMock() and setMethods() is what we need for your test. We only overwrite the one method we want to mock, the rest will behave as defined in the original class. To quote the docs:
setMethods(array $methods) can be called on the Mock Builder object to specify the methods that are to be replaced with a configurable test double. The behavior of the other methods is not changed. If you call setMethods(null), then no methods will be replaced.

Mock builder isnt replacing methods signatures

Aren't mocks supposed to replace methods and their parameters?
I don't understand how this works, I copy-paste examples but i run into trouble on every single example that should work out of the box..
The problem i run into is that the methods do not have their parameters removed, so if a method has a certain dependency as a parameter, i HAVE to pass it to the mock's method too..
The manual even says that by default all methods are replaced by empty methods that return null.
I'm using 5.7(php 5.6)
class SomeClass
{
public function doSomething(\Exception $e)
{
// Do something.
}
}
class StubTest extends TestCase
{
public function manualExampleDoesntWork()
{
// Create a stub for the SomeClass class.
$stub = $this->createMock(SomeClass::class);
// Configure the stub.
$stub->method('doSomething')
->willReturn('foo');
// Calling $stub->doSomething() will now return
// 'foo'.
$this->assertEquals('foo', $stub->doSomething());
}
public function ...
{
// Should be enough, doesnt work
$stub = $this->createMock(SomeClass::class);
$stub->doSomething(); // error
// Neither
$stub = $this->createMock(SomeClass::class);
$stub->method('doSomething')
->withAnyParameters()
->willReturn('foo');
$stub->doSomething(); // error
// Same for manually building with the mockbuilder..
}
}
Results in: Argument 1 passed to Mock_***95::doSomething() must be an instance of Exception, none given
This is just one example, I've tried every possible variation of creating mocks, all results in me not being able to replace the parameters..
It is about method overloading / overriding. PHP does not support this stuff same way as, say, C++ or Java do -- meaning you can't declare methods having same name and different arguments lists either in the scope of current class or further in inheritance chain. Methods signatures must stay the same. One exception is __construct which is overridable with different signature. Should be also mentioned that PHP does have overloading concept but it is a bit different idea. You can read more here and here.
Concerning your particular example all said above means you must call doSomething with either instance of Exception class or with a stub for Exception because the doSomething method of of SomeClass 's stub still has same signature as authentic SomeClass. Also another option could be declaring it as public function doSomething(\Exception $e = NULL).
The implementation of the doSomething method in SomeClass mock is really empty by default -- till you configure it.

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

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.

unit test a method that creates an object

I'm trying to get my head round Unit Testing and there's one more piece of the jigsaw I need to find.
What I'm trying to do is write tests for the following code. In this case, I've got a really simple Front Controller (written in PHP).
class frontController
{
public function routeRequest($oRequest)
{
$sClassname = $oRequest->getController();
$sMethod = $oRequest->getAction();
$oController = new $sClassname();
$oResponse = $oController->{$sMethod}($oRequest);
return $oResponse;
}
}
The problem I have is because the code creates new objects. I can easily mock the request object so that I can tightly control what it will actually do within my test case. I'm not sure the best way to actually replace the controller with a test double.
This article from IBM suggests having a factory method for creating my controller and then overriding this with a specific class used for testing:
class frontController
{
public function routeRequest($oRequest)
{
$sMethod = $oRequest->getAction();
$oController = $this->createController($oRequest);
$oResponse = $oController->{$sMethod}($oRequest);
return $oResponse;
}
protected function createController($oRequest)
{
$sClassname = $oRequest->getController();
return new $sClassname();
}
}
and then for testing perhaps something like this:
class testFrontController extends frontController
{
public function setMockController($oMockController)
{
$this->oMc = $oMockController;
}
protected function createController($oRequest)
{
return $this->oMockController;
}
}
(note this isn't quite what the article says, but I'm thinking it would be most useful to me if it did this)
Another solution could be to have another class that creates the controller. This would then be a dependent class of the frontController. This way I can replace the factory/creation class during testing with a test double. Something like this:
class frontController
{
public function routeRequest($oRequest, $oControllerFactory)
{
$sMethod = $oRequest->getAction();
$oController = $oControllerFactory->create($oRequest);
$oResponse = $oController->{$sMethod}($oRequest);
return $oResponse;
}
}
class controllerFactory
{
public function create($oRequest)
{
$sClassname = $oRequest->getController();
return new $sClassname();
}
}
I guess the dependency injection could be taken care of in the front controller constructor or via a setter instead of a parameter to the actual "route" method.
I think I prefer option 2.
Is either of these two methods the right way of going about testing this kind of thing?
(perhaps "good way" would be better word here!)
Any thoughts or suggestions on option 1 vs option 2 appreciated or indeed any alternatives. Remember - the key thing is about how to test an object that itself creates other objects as part of its execution.
Thanks!
You might find this article handy.
It discusses how object creation should be separated from the actual running of the application.
I generally find factories to be a good thing to use for this scenario. In addition to the swappability aspect, it means that additional parameters, data, or dependencies required by the object being created can be stored by the factory, and so the object which actually requests the new object doesn't have to know anything about them...
You do not want to use the real controller but a mock, right ?
It seems to me the simplest way to achieve this would be to subclass the request so that it returns the name of a MockController.
I assume you have thought through your assertions so as to define the goal of what exactly you are testing. Keep in mind that unit tests are going to be testing the returns from your methods, which, in this case, is $oResponse (whatever this may be). As a result, your test assertions will be based on this return value. Since I don't know what that return value is from your code snippets, I can only demonstrate an example that you can complete.
I would recommend PHPUnit for your testing as it seems to be the most complete package for PHP imho (many are fans of SimpleTest, as well ... to each their own).
It would look something like this (Please note that I have left out includes for brevity. Read the PHPUnit documentation for more information):
class AimTest extends PHPUnit_Framework_TestCase{
private $_controller = null;
private $_request = null;
public function setUp(){
$this->_controller = new frontController();
//what does this object's type?
$this->_request = new requestObject();
}
public function testObjectCreation(){
/*
* note, that this is only one of several assertions that could
* be made depending on the return value
*/
$return = $this->_controller->routeRequest($this->_request);
//tailor to what you expect your output to be
$this->assertTrue($return == "my expected output");
}
Hope I didn't miss the mark completely on your stated purpose. Moral of the story is that you can only test what your methods return. If you want to test object instantiation from a method, use the instanceof PHP function against a method that returns that object after instantiation.

Categories