While trying to get a legacy codebase under test, I've come across an object that does the following:
class Foo
{
public function __construct($someargs)
{
$this->bar = new Bar();
// [lots more code]
}
}
Bar in this instance has a constructor that does some Bad Things e.g. connecting to a database. I'm trying to concentrate on getting this Foo class under test so changed it to something like this:
class Foo
{
public function __construct($someargs)
{
$this->bar = $this->getBarInstance();
// [lots more code]
}
protected function getBarInstance()
{
return new Bar();
}
}
And have attempted to test it via the following PHPUnit test:
class FooTest extends PHPUnit_Framework_TestCase
{
public function testInstance()
{
$bar = $this->getMock('Bar');
$foo = $this->getMock('Foo', array('getBarInstance'));
$foo->expects($this->any())
->method('getBarInstance')
->will($this->returnValue($bar));
}
}
However this doesn't work - the constructor of Foo() is called before my ->expects() is added, so the mocked getBarInstance() method returns a null.
Is there any way of unlinking this dependency without having to refactor the way the class uses constructors?
Use the $callOriginalConstructor argument of getMock(). Set it to false. It's the fifth argument of the method. Look it up here: http://www.phpunit.de/manual/current/en/api.html#api.testcase.tables.api
Actually, hold on. You want to pass a mock to a mock? If you really want this, then use the third argument of getMock which represents the arguments to the constructor. There you can pass the mock of Bar to the mock of Foo.
Related
I have a constructor in the class someClass that calls a function (initObjectFunc), which its only purpose is to return a newly created instance of Thing class, and places its that object into a property (thingObject) in the constructor.
class someClass {
public function __construct() {
$this->thingObject = $this->initObjectFunc();
}
function initObjectFunc() {
return new Thing();
}
}
class Thing {
public function __construct() {}
function funcB() {
... funcB normal implementation ...
return value;
}
}
I also have a separate function (funcA), also in the someClass class, that uses thingObject to call its inner function (funcB).
.
.
.
function funcA() {
$data = $this->thingObject->funcB();
... other stuff ....
}
In a PHPUnit test class, I had mocked the Thing class and its funcB to return mocked data ('test test test test') when it gets called through the execution of funcA. To connect all the pieces together, I mocked the initObjectFunc to return that mocked Thing class object.
class unitTest extends PHPUnit_Framework_TestCase {
$mockThing = $this->getMockBuilder('Thing')
->setMethods(array('funcB'))
->getMock()
->expects($this->any())
->method('funcB')
->will($this->returnValue('test test test test'));
$mockSomeClass = $this->getMockBuilder('someClass')
->setMethods(array('initObjectFunc'))
->getMock()
->expects($this->any())
->method('initObjectFunc')
->will($this->returnValue($mockThing));
}
This setup was created with this execution flow in mind:
$mockSomeClass calls-> funcA() calls-> *mocked*funcB through *mocked*Thing class
Use the $mockSomeClass to call funcA (which, in PHPUnit, should not have been stubbed and should have full functionality), which would call the funcB. However, since I mocked Thing class and its funcB, it should be calling the mocked funcB to return 'test test test test'.
This is not the case when I try to run the tests. It breaks without any errors. Not even the line that says how many tests, assertions, skips, & fails have occurred.
I had also tried this variation, but to no avail:
class someClass {
public function __construct() {}
function initObjectFunc() {
return new Thing();
}
function funcA() {
$thingObject = initObjectFunc();
$data = $thingObject->funcB();
... other stuff ....
}
}
This variation tries to take the functionality from the constructor and move it into funcA so that the initObjectFunc() gets called and should return a mocked Thing instance before using it to call funcB. This was done under the thought that maybe the someClass constructor gets called before any of the mocks can be made.
I don't know why or how these components are not connecting. Has anyone tried unit testing with this structure and achieved success? I know that a simple refactor of code to become "dependency injection"-friendly would solve this issue, but this is a question just to find out if this path to unit testing works.
I want to test a method of a class that instantiates another object
and calls a method of this object.
How can I mock this object and its method Foo2 and run() without dependency injection?
Is this possible or do I need to modify the code for Foo class to inject the object?
class Foo {
public function bar()
{
$foo2 = new Foo2();
$data = $foo2->run();
}
}
I recently find a nice features in Mockery (a mock object framework for PHP) called Mocking Hard Dependencies (new Keyword) that permit you to overload/mock class instantiated in a method. As Example:
use Mockery as m;
class BarTest extends \PHPUnit_Framework_TestCase
{
public function testBar()
{
$param = 'Testing';
$externalMock = m::mock('overload:Foo\Bar2');
$externalMock->shouldReceive('run')
->once()
->andReturn('Tested!');
$foo = new Foo();
$foo->bar();
}
}
Hope this help
use the mock-facilities of phpunit:
However, you should consider to split your bar() methods logic into two separate methods to split the instantiation and the execution, e.g.:
bar() {
$foo2 = $this->getFooImplementation();
$data = $foo2->run();
}
you're then able to mock the call of getFooImplementation():
$fooInstance = $this->getMockBuilder('Foo')->setMethods(array('getFooImplementation'))->getMock();
$fooInstance->expects($this->at(0))->method('getFooImplementation')->willReturn(new Foo3());
I have a DB interface object, DBI, that requires authentication. I have an object, foo, that doesn't extend anything. I also have a class bar that extends the DBobject If I have an instance of foo that is a member of bar thus:
$b->$f=new foo;
How can I call somefunction() in $b from a function inside the foo class? I've tried making the somefunction() static, but I don't want the authentication info sprinkled throughout my code. And if I try having foo extend the DBI or bar classes, I wind up with an issue including the files and my foo __construct function fails because the bar class is not found. Is there another construct similar to extends/parent:: that I can use with objects that are just instances amongst each other?
The way that I've done this in the past, is create the Foo object within the Bar object within the __construct(). Then utilize the __call magic method to intercept the methods and see where it is. So the code could look something like this:
public function __call($sMethod, $aArgs) {
if(method_exists($this, $sMethod){
return call_user_func_array(array($this, $sMethod), $aArgs);
}
} elseif(method_exists($this->foo, $sMethod)) {
return call_user_func_array(array($this->foo, $sMethod), $aArgs);
}
}
public function __construct() {
$this->foo = new foo();
}
Then you can call the functions from either foo or bar, even though they are not extended. Just a though, maybe there is an easier way to do this.
** EDIT **
The benefit to this is you don't need to specify whether or not you are calling a method from foo or from bar, they will just "work".
** EDIT **
Based on the comments, you want to do this, correct? Because based on the code below, if you run it it works correctly.
class foobar {
public function test() {
echo 'This is a test';
}
}
class foo extends foobar {
}
class bar {
}
$bar = new bar();
$bar->foo = new foo();
$bar->foo->test();
or the alternative:
class foobar {
public function test() {
echo 'This is a test';
}
}
class foo extends foobar {
}
class bar {
public function testFooBar() {
$this->foo->test();
}
}
$bar = new bar();
$bar->foo = new foo();
$bar->testFooBar();
Both work at long as you know the property name you are setting for the object.
In addition to call_user_func and call_user_func_array, if you want to access methods and properties of the container object (not parent class), you need a reference to it. Here is a similar post.
I'm trying to test a static method on a class, but it relies on a static array property that doesn't get reset between tests, so I though I could create a mock class & test against that.
The only problem is that the mock class static method is not returning the value as the original static method does.
Here's my class...
class Thing {
static public function doThing() {
return 'yeah!';
}
}
... and here's be test class...
class ThingTest {
public function testDoSomething() {
$mock_class = $this->getMockClass('Thing');
$this->assertEqual('yeah!', $mock_class::doThing())
}
}
This test fails with the message "Failed asserting that null matches expected 'yeah!'."
What am I missing here? I thought not specifying the methods on the getMock() call meant that the original methods carried over, but clearly not. :o(
Any pointers would be great. Thanks.
Edit
I typed this up from memory, rather than from copying code, as I'd made further change to try to resolve this. After writing more tests for mock objects I realised that if you pass array() as the second parameter ($methods) to getMock[Class] then it stubbed out all the methods, & I believe this is what I had actually done. I was doing this, as I also wanted to pass the constructor parameter in the 3rd argument. The code probably looked more like this...
class ThingTest {
public function testDoSomething() {
$mock_class = $this->getMockClass(
'Thing',
array(),
array( 'something' )
);
$this->assertEqual('yeah!', $mock_class::doThing())
}
}
Maybe $mock_class is an object, not a string class name? Try to use next:
class ThingTest {
public function testDoSomething() {
$mock_class = $this->getMockClass('Thing');
$this->assertEqual('yeah!', Thing::doThing())
}
}
Is this work?
You forgot extending by PHPUnit_Framework_TestCase.
class ThingTest extends PHPUnit_Framework_TestCase {
public function testDoSomething() {
$mock_class = $this->getMockClass('Thing');
$this->assertEqual('yeah!', $mock_class::doThing())
}
}
You shouldn't mock the class containing the method you want to test. If you mock the class, you can't call any method either before you haven't defined it via
-> expects('methodName')
-> with($this->equalTo($parameter),[$this->equalTo($parameter2),...])
-> will($this->returnValue($value))
. Mocking is used to reduce complexity of a test. Let's say you want to test a function that looks like this:
class test{
private $someClass;
public function __construct(SomeInterface $someClass){
$this -> someClass = $someClass;
}
public function test(){
$someVariable = $this -> someClass -> doSomething();
changeSomething($someVariable);
return $someVariable;
}
}
If you don't mock someClass, you'd have to test the whole code of someClass -> doSomething. But that is not what you want to do. You just want to use the return of the function doSomething. That is the point where you need a mock. It reduces the complexity of your test and offers REAL UNIT tests instead of testing the whole app.
So what you need to do is:
class ThingTest {
public function testDoSomething() {
$class = new Thing();
$this->assertEquals('yeah!', $class::doThing());
}
}
There is no need for a mock.
I've recently been given the task of getting our system set up for unit testing but there's one problem I'm having. The example at http://sebastian-bergmann.de/archives/885-Stubbing-Hard-Coded-Dependencies.html is setup very similar to the code I'm trying to test. The only other thing that I need to do is to have doSomethingElse() return something of my choosing.
UPDATE
I have tried setting the getMock to some variable and then setting the return value. But PHPUnit doesn't seem to notice the variable and the return value I've set. This is because, I'm assuming, that the variable is never used anywhere. But with how the code is set up, there is no way to pass the mock object into foo so it knows to use that instead of the actual Bar class. I've added in the Foo and Bar class for easier reference. The original Bar class returns * but I want it to return ** from the mock class.
Using set_new_overload() isn't mandatory for this test but so far using it is the only way I've been able to successfully mock Bar.
<?php
require_once 'Foo.php';
class FooTest extends PHPUnit_Framework_TestCase {
protected function setUp()
{
$mock = $this->getMock(
'Bar', /* name of class to mock */
array('doSomethingElse'), /* list of methods to mock */
array(), /* constructor arguments */
'BarMock' /* name for mocked class */
);
$mock->expects($this->any())
->method("doSomthingElse")
->will($this->returnValue("**")
);
set_new_overload(array($this, 'newCallback'));
}
protected function tearDown()
{
unset_new_overload();
}
protected function newCallback($className)
{
switch ($className) {
case 'Bar':
return 'BarMock';
default:
return $className;
}
}
public function testDoSomething()
{
$foo = new Foo;
$this->assertTrue($foo->doSomething());
}
}
?>
<?php
require_once 'Bar.php';
class Foo {
public function doSomething()
{
// ...
$bar = new Bar;
$bar->doSomethingElse();
// ...
return TRUE;
}
}
?>
<?php
class Bar {
public function doSomethingElse()
{
return '*';
}
}
?>
The mock class won't have any expected behavior by itself. To use it as a stub (do X when method Y is called) you need access to the BarMock instance created, but it doesn't look like the new overload will give you that.
Instead I would create a custom subclass of Bar that returns what you want from doSomethingElse().
I don't really understand what you're trying to do here, but:
You're calling getMock() but not actually assigning to anything
To set a return value, see Example 11.2: http://www.phpunit.de/manual/3.5/en/test-doubles.html
You have some odd whitespace in ' BarMock'