PHPUnit: include class after mocking it - php

I'm happily writing unit tests, but they clash when I run them all together. I'm testing this class:
class MyClass {
public function sayHello() {
return 'Hello world';
}
}
using this test. All tests have a structure like this:
class MyClassTest extends PHPUnit_Framework_TestCase {
private $subject;
public static function setUpBeforeClass() {
require_once('path/to/MyClass.php');
}
public function setUp() {
$this->subject = new MyClass();
}
public function testSayHello() {
$this->assertEquals('Hello world', $this->subject->sayHello());
}
}
MyClassTest runs fine on its own. But PHPUnit will crash because I redeclare the class, if another test mocks MyClass and happens to run first:
class Inject {
private $dependency;
public function __construct(MyClass $myClass) {
$this->dependency = $myClass;
}
public function printGreetings() {
return $this->dependency->sayHello();
}
}
class InjectTest extends PHPUnit_Framework_TestCase {
public function testPrintGreetings() {
$myClassMock = $this
->getMockBuilder('MyClass')
->setMethods(array('sayHello'))
->getMock();
$myClassMock
->expects($this->once())
->method('sayHello')
->will($this->returnValue(TRUE));
$subject = new Inject($myClassMock);
$this->assertEquals('Hello world', $subject->printGreetings());
}
}
I do use a bootstrap.php to fake some global functions not yet refactored.
I have no auto loaders and don't want to process-isolate EVERY test, because it takes forever. I tried inserting combinations #runTestsInSeparateProcesses and #preserveGlobalState enabled/disabled in the docblocks of both Test 1 & 2, I still get the same error.

To understand this behaviour, you need to have a look at how PHPUnit works. getMockBuilder()->getMock(), dynamically creates the following code for the mock class:
class Mock_MyClass_2568ab4c extends MyClass implements PHPUnit_Framework_MockObject_MockObject
{
private static $__phpunit_staticInvocationMocker;
private $__phpunit_invocationMocker;
public function __clone()
{
$this->__phpunit_invocationMocker = clone $this->__phpunit_getInvocationMocker();
}
public function sayHello()
{
$arguments = array();
$count = func_num_args();
if ($count > 0) {
$_arguments = func_get_ ...
# more lines follow ...
If MyClass hasn't already been loaded at this time, it adds the following dummy declaration:
class MyClass
{
}
This code will then getting parsed using eval() (!).
Since PHPUnit will execute InjectTest before MyClassTest, this means that the mock builder will define the dummy class and MyClass is already defined when MyClassTest::setUpBeforeClass will get called. That's why the error. I hope I could explain. Otherwise, dig into PHPUnit's code.
Solution:
Drop the setUpBeforeClass() method and put the require_once statement on top of the tests. setUpBeforeClass() is not meant for including classes. Refer to the docs.
Btw, having require_once on top will work because PHPUnit will include every test file before starting the first test.
tests/MyClassTest.php
require_once __DIR__ . '/../src/MyClass.php';
class MyClassTest extends PHPUnit_Framework_TestCase {
private $subject;
public function setUp() {
$this->subject = new MyClass();
}
public function testSayHello() {
$this->assertEquals('Hello world', $this->subject->sayHello());
}
}
tests/InjectTest.php
require_once __DIR__ . '/../src/Inject.php';
class InjectTest extends PHPUnit_Framework_TestCase {
public function testPrintGreetings() {
$myClassMock = $this
->getMockBuilder('MyClass')
->setMethods(array('sayHello'))
->getMock();
$myClassMock
->expects($this->once())
->method('sayHello')
->will($this->returnValue(TRUE));
$subject = new Inject($myClassMock);
$this->assertEquals(TRUE, $subject->printGreetings());
}
}

Related

Repository pattern php - is interface needed?

I have the following code and I don't know how to use the interface, I feel like I don't need it but I want to
I am using only composer for autoloading without any framework
service code (run from batch file from the example)
try {
$test = new testClass(); //how do I put here the interface , i dont want to put it here
$test->run();
}
catch(\Exception $e) {
echo $e->getMessage() . "\n";
die;
}
Test Class:
class TestClass extends AbstractClass
{
private $_repo;
public function __construct(RepositoryInterface $repo)
{
parent::__construct();
$this->_repo = $repo;
}
public function run(){
}
AbstractClass
abstract class AbstractClass
{
protected $logger;
protected $db;
public function __construct()
{
Configuration::config();
$this->db = PDOConnection::dbConnect();
}
it is not working, now I just call to the testRepository that implement the interface directly, without calling to the interface
so how can I register the interface to the constructor or I have to call it each place I initiate a testclass object
thanks
In your TestClass method construct need one argument.
public function __construct(RepositoryInterface $repo)
When you create object of class you did not use any arguments:
$test = new testClass();
Then add argument in new testClass($repo); or change __construct method like
public function __construct(RepositoryInterface $repo = null)
I think you're missing some understanding. #J.Litvak's answer does not provide this info either.
class TestClass extends AbstractClass
{
private $_repo;
public function __construct(RepositoryInterface $repo)
{
parent::__construct();
$this->_repo = $repo;
}
}
This class has a constructor which requires an object which implements the RepositoryInterface.
As such, the variable you pass must be an object, and it must have used that Interface. With me so far?
This means that the following code is incorrect.
try {
$test = new testClass();
$test->run();
} catch(\Exception $e) { ... }
The line initializing the $test variable with an instance of TestClass, must an object which uses the RepositoryInterface for the TestClass::__construct function.
So, somewhere, you must create or have:
class CustomRepository implements RepositoryInterface
{
public function findAll() { ... }
}
Now, update your try{} / catch () {} to the following:
$customRepository = new CustomerRepository();
try {
$test = new testClass($customRepository);
$test->run();
} catch(\Exception $e) { ... }
Which should work, as the CustomRepository class implements the interface you require in the TestClass::__construct parameters.
Have a look at some of the PHP docs:
interfaces
constructors and destructors
I figured it out with PHP DI module and inject it via composer
something like :
return [
// Bind an interface to an implementation
RepositoryInterface::class => object(Repository::class),
];

Phpunit mock only one method in tested class - using Mockery

I'm learning phpunit since week. I don't have idea how to mock only one method from tested class. (it's example only so I didn't write namespaces). Maybe you can help me
class SomeService
{
public function firstMethod()
{
return 'smth';
}
public function secondMethd()
{
return $this->firstMethod() . ' plus some text example';
}
}
and test:
class SomeServiceUnitTest extends TestCase
{
private $someService;
public function setUp()
{
parent::setUp();
$this->someService = new SomeService();
}
public function tearDown()
{
$this->someService = null;
parent::tearDown();
}
public function test_secondMethod()
{
$mock = Mockery::mock('App\Services\SomeService');
$mock->shouldReceive('firstMethod')->andReturn('rerg');
exit($this->walletService->secondMethd());
}
}
You can use a partial mocks, as example on your test class, you can do:
public function test_secondMethod()
{
$mock = Mockery::mock('App\Services\SomeService')->makePartial();
$mock->shouldReceive('firstMethod')->andReturn('rerg');
$this->assertEquals('rerg plus some text example', $mock->secondMethd());
}
Hope this help

PHP Unit: Create common object to work with

I'm writing PHP Unit tests for a class, which make some curl requests. At the moment every test starts with my class instance initiation and login directive and ends with logout directive, e.g.
public function testSomeMethod(){
$a = new myClass();
$a->login();
....
$a->logout();
$this->assertTrue($smth);
I want to create one common object $a = new myClass(), call login method before all test and logout method after all tests. How can I do that?
In accordion with the PHPUnit documentation here you can use the following hook method:
The setUp() and tearDown() template methods are run once for each test
method (and on fresh instances) of the test case class.
Also
In addition, the setUpBeforeClass() and tearDownAfterClass() template
methods are called before the first test of the test case class is run
and after the last test of the test case class is run, respectively.
In your case you can define the login class as class member and instantiate (login) in the setUpBeforeClass() method and do the logout in the tearDownAfterClass()
EDIT: EXAMPLE
Consider the following class:
namespace Acme\DemoBundle\Service;
class MyService {
public function login()
{
echo 'login called'.PHP_EOL;
}
public function logout()
{
echo 'logout called'.PHP_EOL;
}
public function doService($name)
{
echo $name.PHP_EOL;
return $name;
}
}
This test case:
use Acme\DemoBundle\Service\MyService;
class MyServiceTest extends \PHPUnit_Framework_TestCase {
/**
* #var \Acme\DemoBundle\Service\MyService
*/
protected static $client;
public static function setUpBeforeClass()
{
self::$client = new MyService();
self::$client->login();
}
public function testSomeMethod1()
{
$value = self::$client->doService("test1");
$this->assertEquals("test1",$value);
}
public function testSomeMethod2()
{
$value = self::$client->doService("test2");
$this->assertEquals("test2",$value);
}
public static function tearDownAfterClass()
{
self::$client->logout();
}
}
Dump the following output:
login called .test1 .test2 logout called
Time: 49 ms, Memory: 6.00Mb
OK (2 tests, 2 assertions)
hope this help
Creating reusable / common object at class level ( to be use in methods/functions) answer by #Matteo was helpful, Here is my implementation
no need for multiple inheritance , or __construct() constructor, I spent a lot of time on that ( specially coming from java background)
<?php
namespace Tests\Unit;
use Tests\TestCase;
use App\Services\HelperService;
class HelperServiceTest extends TestCase
{
//Object that will be reused in function()'s of this HelperServiceTest class itself
protected static $HelperServiceObj;
//this is the magic function
public static function setUpBeforeClass()
{
self::$HelperServiceObj = new HelperService();
}
public function testOneHelperServiceToBeTrue()
{
$this->assertTrue(self::$HelperServiceObj->getValue());
}
public function testTwoHelperServiceToBeTrue()
{
$this->assertTrue(self::$HelperServiceObj->getValueTwo());
}
}
Refer official docs setUp() , setUpBeforeClass() , tearDown() for magic function like setUpBeforeClass()
Sample implementation of getValue() function in HelperServiceTest class
<?php
namespace App\Services;
class HelperServiceTest
{
function getValue(){
return true;
}
function getValueTwo(){
return true;
}
}

How to share objects in different test in PHPUnit

I have the following class 'MyControllerTest' for testing purposes of 'MyController'.
I want to share the same object which is '$algorithms' in different tests of this classes but I don't know how to do that after trying adding the variable in different places:
class MyControllerTest extends Zend_Test_PHPUnit_ControllerTestCase
{
// $algorithms = ... <----- does not work
public static function main()
{
$suite = new PHPUnit_Framework_TestSuite("MyControllerTest");
$result = PHPUnit_TextUI_TestRunner::run($suite);
}
public function setUp()
{
$this->bootstrap = new Zend_Application(
'testing',
APPLICATION_PATH . '/configs/application.ini'
);
$configuration = Zend_Registry::get('configuration');
// $algorithms = ... <----- does not work
parent::setUp();
}
public function tearDown()
{
}
public function test1() {
// this array shared in different tests
$algorithms[0] = array(
"class" => "Mobile",
"singleton" => "true",
"params" => array(
"mobile" => "true",
"phone" => "false"
)
);
...
}
public function test2() { };
}
How can I share this object?
Any help would be appreciated!
You've got a couple of options here
Declare the data fixture right into the setUp method and declare a private variable that holds it. So you can use it in the other test methods.
Declare a private function within your test class that holds your data fixture. If you need the data fixture just call the private method
Create a Utiltity class with a method that holds the data fixture. The Utility class is initialized in the setUp function. Declare a private variable within the MyControllerTest that holds the instance of the Utility class. When a test needs the fixture just call the fixture method from the Utility instance.
Example 1
class MyControllerTest extends Zend_Test_PHPUnit_ControllerTestCase
{
private $alg;
public function setUp()
{
# variable that holds the data fixture
$this->alg = array(....);
}
public function test1()
{
$this->assertCount(1, $this->alg);
}
}
Example 2
class MyControllerTest extends Zend_Test_PHPUnit_ControllerTestCase
{
public function test1()
{
$this->assertCount(1, $this->getAlgo());
}
# Function that holds the data fixture
private function getAlgo()
{
return array(....);
}
}
Example 3
class Utility
{
# Function that holds the data fixture
public function getAlgo()
{
return array(....);
}
}
class MyControllerTest extends Zend_Test_PHPUnit_ControllerTestCase
{
private $utility;
public function setUp()
{
$this->utility = new Utility();
}
public function test1()
{
$this->assertCount(1, $this->utility->getAlgo());
}
}
But beware of fixtures that are shared and are altered in some tests. This can really mess up your test suite.

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'));
...
}
}

Categories