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());
Related
Is there any difference in constructor or unified constructor. I have a class which have a constructor and a unified constructor. When i intialise object of the class then unified constructor call why not normal constructor.
<?php
class test {
function __construct() {
print "In BaseClass constructor\n";
}
function test() {
echo 'Class loeaded';
}
}
$obj = new test();
?>
OUTPUI
In BaseClass constructor
PHP Version 5.2.6
Since PHP 5, the best way to declare a constructor method in a class is to use the standardized
__construct()
So if you have
class myA {
public function __construct() {
echo "hello construct myA";
}
public function myA() {
echo "hello basic way myA";
}
}
$myA = new myA();
It will echo
hello construct myA
because the __constructor() has priority
But, for compatibility reason, the old way may work if there is not __construct() method.
And:
class myA {
//public function __construct() {
// echo "hello construct A";
//}
public function myA() {
echo "hello basic way myA";
}
}
$myA = new myA();
will give you:
hello basic way myA
In all cases, I advise you to use __construct()
the old constructor (method with class name) is only there for compatibility reasons. so if you have the new constructor (__construct) in the class php won't bother to call the old one.
edit
interesting note is that calling parent::__construct when the class being extended has only the old constructor type will still work (it becomes like a alias to the real constructor)
Classes which have a constructor method call this method on each newly-created object, so it is suitable for any initialization that the object may need before it is used.
Read documentation of constructor (link)
Instances of classes are created using the new keyword. What happens during the new call is that a new object is allocated with its own copies of the properties defined in the class you requested, and then the constructor of the object is called in case one was defined. The constructor is a method named __construct(), which is automatically called by the new keyword after creating the object. It is usually used to automatically perform various initializations such as property initializations. Constructors can also accept arguments, in which case, when the new statement is written, you also need to send the constructor the function parameters in between the parentheses.
In PHP 4, instead of using __construct() as the constructor’s name, you had to define a method with the classes’ names, like C++. This still works with PHP 5, but you should use the new unified constructor naming convention for new applications.
We could pass the names of the people on the new line in the following class example :
class Person {
function __construct($name)
{
$this->name = $name;
}
function getName()
{
return $this->name;
}
private $name;
};
$judy = new Person("Judy") . "\n";
$joe = new Person("Joe") . "\n";
print $judy->getName();
print $joe->getName();
Tip: Because a constructor cannot return a value, the most common practice for raising an error from within the constructor is by throwing an exception.
According to documentation As of PHP 5.3.3, methods with the same name as that of class will no longer be treated as constructor .. They are like regular methods ...
<?php
class myclass
{
public function myclass()
{
// treated as constructor in PHP 5.3.0-5.3.2
// treated as regular method as of PHP 5.3.3
}
}
?>
`
For backwards compatibility, if PHP 5 cannot find a __construct() function for a given class, and the class did not inherit one from a parent class, it will search for the old-style constructor function, by the name of the class.
It means that there if you have constructor myclass() and __construct .. then __construct will be searched for first and taken as constructor instead of myclass()
class myclass
{
public function myclass()
{
echo 'hello';
}
function __construct() {
print "new constructor"
}
}
$obj = new myclass(); // will echo new constructor
I'm using spl_autoload for dependency injection.
spl_autoload_register(function ($class)
{
$cFilePath = _CLASSLIB_ . "/class.$class.php";
if(file_exists($cFilePath))
{
include($cFilePath);
}
else
{
die("Unable to include the $class class.");
}
});
This works fine. However, let's say these are my classes:
class Test
{
public function foo()
{
echo "Here.";
}
}
And
class OtherTest
{
public function bar()
{
global $Test;
$Test->foo();
}
}
So, in my executing code:
<?php
$OT = new OtherTest(); //Dependency Injection works and loads the file.
$OT->bar();
?>
I'll get an error because bar() tries to global in the test class (which wasn't instantiated and, thus, never autoloaded).
What is the best way to implement this other than checking to see if the $Test global is an object before trying to use it in every method?
Avoid using globals if at all possible. You mentioned Dependency Injection in your comment: You can use DI to solve this issue.
If OtherTest depends on an instance of Test, that instance of Test should be supplied to OtherTest when it is constructed, e.g.
$T = new OtherTest($Test);
You'd obviously need to amend your OtherTest class to have an instance of Test as a property, and a constructor that takes a Test as an argument, something like:
class OtherTest
{
protected $test = null;
public function __construct(Test $test)
{
$this->test = $test;
}
public function bar()
{
return $this->test->foo();
}
}
You could then do the following:
$test = new Test();
$otherTest = new OtherTest($test);
$otherTest->bar();
I think you are confusing what is meant by dependency injection. Class autoloading is not dependency injection. Dependency injection is where you actually inject a dependency that an object might have into the object so that it can work with it. The object receiving the dependency is thus decoupled from needing to create its dependency at all.
The best way to do achieve dependency injection in this case would be to ACTUALLY inject the dependency on Test class into OtherTest on OtherTest instantiation. So Othertest might look like this:
class OtherTest
{
protected $test_object = NULL;
public function __construct($test_obj) {
if ($test_obj instanceof Test === false) {
throw new Exception('I need a Test object');
}
$this->test_obj = $test_obj;
}
public function bar()
{
$this->$test_obj->foo();
}
}
And the code to instantiate might look like:
$OT = new OtherTest(new Test()); // both OtherTest and Test would be autoloaded here if not previously loaded.
Note that referring to an undeclared variable ($Test in your example) isn't going to autoload a class, as the variable name by itself has no context of a class. You would just end up getting an error for trying to call a method on a non-object.
Let's suppose I have a parent class in PHP like this:
class A {
private $property;
public static function factory($arg) {
$object = new A();
$object->property = $arg;
return $object;
}
}
And I want to extend it in this way:
class B extends A {
public static function factory() {
return parent::factory('constant');
}
}
When I do B::factory() I get a object of type A. What if I want an object of type B? I cannot change anything in the code of class A.
1st version
That's because you hardcoded the A class in the factory method.
In class A, instead of $object = new A() try (require Php 5.3):
$class_name = get_called_class();
$object = new $class_name;
get_called_class() "Gets the name of the class the static method is called in."
Shorter version:
$object = new static();
2nd version (hardcoded parent class):
Copy object properties manually:
$parent = parent::factory($args);
$obj = new static();
$obj->setTimestamp($parent->getTimestamp());
$obj->setTimezone($parent->getTimezone());
return $obj;
Or use an hack to do it autoatically:
How to Cast Objects in PHP
In your example:
You have two classes (unleast)
both classes can be instantiated (concrete, not pure or abstract)
one class is a superclass of another
both classes are instantiated with a "factory" method
the "factory" method of a subclass can invoke the the "factory" method of the superclass
each "factory" method can have several type or count of parameters
Problem
Now, this is what it got my attention:
class B extends A {
public static function factory() {
return parent::factory('constant');
}
}
Short & Quick answer:
Change that to:
class B extends A {
public static function factory() {
return A::factory('constant');
}
}
Long boring hipster extended answer:
You are attempting to overriding ( and overloading, with different parameters ) a static function.
Its a common mistake, that assume that static methods are virtual, and can be overrided. Some programming languages allow that (Object Pascal, Delphi), other don't (C#, Java), PHP depends on the version, I think.
To be honest, "static functions" work similar like global functions with a namespace, that have public access to all members of a class, Instead of methods. I suggest to see them as global functions, always.
Cheers.
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.
class foo(){
function bar()
{
$classInstance = $this->createClassInstance($params);
$result = $classInstance->getSomething();
}
function createClassInstance($params)
{
require 'path/to/class.php';
$myClass = new Class;
$myClass->acceptParams($params['1']);
$myClass->acceptMoreParams($params['2']);
.... lots more params
return $myClass;
}
}
Can I initiate a new class by calling a method that returns a class object? The class in question has lots of parameters and I need to call it multiple times within bar() so I thought it would be neater to do it that way, but I can't get it working and want to check if it's possible + good practice?
That's called factory class (Factory OO Design Pattern).
How it should be done in PHP: http://www.php.net/manual/en/language.oop5.patterns.php
What I think you're describing is the Factory pattern, but you're using parameters to set the class variables just like you would in a constructor, so why not just use that?
Edit:
Ah, if you're using the same parameters for the most part then you definitely want the Factory pattern. Just use a static function to return an instance of the class, and put it inside the type of class you're returning:
class MyClass
{
public static function factory($params)
{
$myClass = new MyClass;
$myClass->acceptParams($params['1']);
$myClass->acceptMoreParams($params['2']);
//.... lots more params
return $myClass;
}
}
class foo(){
function bar()
{
$classInstance = MyClass::factory(param1, param2);
}
}