If I have a class A that interacts with class B then in my tests I want to mock class B so that I can test class A in isolation.
This is easy to do in PHPUnit with "getMock('classname')".
My problem currently is: If class A uses multiple instances of class B I can't simulate this with "getMock('B')" because it appears that "getMock" will not return multiple instances if called multiple times but always the same mock of class B.
Following Example:
<?php
class A()
{
private class_b_1;
private class_b_2;
public function setClassB1(B $class_b)
{
$this->class_b_1 = $class_b;
}
public function setClassB2(B $class_b)
{
$this->class_b_2 = $class_b;
}
}
And in my tests:
$mock_one_of_class_b = $this -> getMock('B');
$mock_two_of_class_b = $this -> getMock('B');
Then $mock_one_of_class_b is the same object as $mock_two_of_class_b.
How can I mock multiple instances of a class with PHPUnit?
Thanks in advance!
Actually the getMock method creates different instances of mocked class. Take a look on this example:
class Foo
{
protected $value;
public function setValue($value)
{
$this->value = $value;
}
public function getValue()
{
return $this->value;
}
}
Now we create test for it:
class FooTest extends \PHPUnit_Framework_TestCase
{
public function testFoo()
{
$a = $this->getMock('Foo', array('someNonExistingMethod'));
$b = $this->getMock('Foo', array('someNonExistingMethod'));
$a->setValue(1);
$b->setValue(234);
$this->assertEquals(1, $a->getValue(), 'This test will fail if "a" and "b" objects are the same');
}
}
Pay attention on the second argument in getMock method. In that argument you tell phpunit which methods will be mocked. If you don't pass any argument at all - then phpunit assume you want mock all methods from the object. So, if you pass array('someNonExistingMethod') then there won't be any mocked method (and "real" methods will be called on invocations).
Related
I have 2 classes declared like in the example below.
class A{
protected $process;
public function __construct() {
......
$this->process=new B();
}
public function do_something(){
....
}
}
class B{
// content not important
// I need to call do_something from class A
}
My question is, how can I call from class B the method do_something() from class A? Is it possible?
From your example it is impossible for instance of B to know that it is instantiated and stored by an instance of class A. You need to create that connection explicitly in some way.
I didn't think this would even work, but apparently you can pass instance of A to B before A is even done with its constructor:
class A {
protected $process;
public function __construct() {
$this->process = new B( $this );
}
public function do_something() {
var_dump( 'do_something' );
}
public function test() {
$this->process->test();
}
}
class B {
public function __construct( A $a ) {
$this->a = $a;
}
public function test() {
$this->a->do_something();
}
}
$a = new A();
$a->test(); // do_something
It's hard to give an advice on what the best approach for your particular case would be, as we don't know what either A or B does.
There's a few ways to achieve this. One way would be to make B and extension of A - thereby allowing all methods of the class A to be callable on the object B. Another way is to create a new object of A inside B and call that method. Or you can pass
Here's an example where B is extended from A. By doing this, all properties and methods of A can be called on B, unless overwritten in B.
class A {
public function doSomething(){
echo "doSomething() called in A";
}
}
class B extends A {
public function someMethod() {
$this->doSomething();
}
}
$b = new B();
$b->someMethod();
The above would output doSomething() called in A.
Or, you can create an object A and call that method inside B.
class B {
public function someMethod() {
$a = new A();
$a->do_something();
}
}
$b = new B();
$b->someMethod();
After reading all the answers and doing some research i think that the best method for me was the use of Traits
"Traits are a mechanism for code reuse in single inheritance languages such as PHP. A Trait is intended to reduce some limitations of single inheritance by enabling a developer to reuse sets of methods freely in several independent classes living in different class hierarchies."
So i declared a Trait with the method do_something and call that method from Both class A and Class B
Thanks
I can't get Mockery to create a simple dummy:
<?php
require_once '../vendor/autoload.php'; // composer autoload mockery
class Foo {
private $required;
public function __construct($required){
$this->required = $required;
}
public function bar(){
// do stuff with $this->required
}
}
class FooTest extends PHPUnit_Framework_TestCase {
public function testBar(){
$mock = \Mockery::mock('\Foo');
$mock->bar();
}
}
Running that PHPUnit test gives the error:
BadMethodCallException: Method Mockery_0_Foo::bar() does not exist on this mock object
What am I doing wrong?
If you'd like to do php unit testing on "Foo" class and mocks "Required" object. Just do it like below:
class Foo {
private $required;
public function __construct(\Required $required){
$this->required = $required;
}
public function bar(){
return $this->required->getTextFromBarTable();
}
}
class FooTest extends PHPUnit_Framework_TestCase {
public function testBar(){
$mock = \Mockery::mock('\Required'); // Dummy, There are no properties or methods.
/**
* Stub "getTextFromBarTable" method of \Required class
* and fakes response by returning "return this text".
*/
$mock->shouldReceive('getTextFromBarTable')
->andReturn('return this text');
// create "Foo" Object by using $mock instead of actual "\Required" Object.
$foo = new Foo($mock);
$response = $foo->bar();
$this->assertEqual('return this text', $response);
}
}
You must NOT stub or mock class that you want to do unit testing on. Just do it on Dependency Class like "\Required".
we do STUB or MOCK to separate EXTERNAL logic that could affect INTERNAL logic of method we're going to test. In this case I assumed \Required class has "getTextFromBarTable" method and this method will connect and get 'text' field from database. The "testBar" method will be broken if our database doesn't have text field. To get rid of external problems I did stub on "\Required" and every times I use "getTextFromBarTable" method. It will always return me "return this text".
I had to explicitly state what methods the mock makes stubs of:
class FooTest extends PHPUnit_Framework_TestCase {
public function testBar(){
$mock = \Mockery::mock('Foo');
$mock->shouldReceive('bar');
$mock->bar();
}
}
I'm curious if there's a way round this, something which either:
implicitly catches all method calls that are defined in Foo, or
implicitly catches all method calls, whether or not they are defined in Foo
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.
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'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'));
...
}
}