I'm trying to test a private method in an abstract class.
I've got three abstract classes:
abstract class AbstractClass1 extends AbstractClass2
{
private function _privateFunction()
{
//method's body
}
}
abstract class AbstractClass2 extends AbstractClass3
{
public function __construct($param)
{
parent::__construct($param)
}
}
abstract class AbstractClass3
{
public function __construct($param = array())
{
//something
}
}
The test class:
class AbstractClass1Test extends PHPUnit_Framework_TestCase
{
public function test_privateFunction()
{
$stub = $this->getMockForAbstractClass("AbstractClass1");
$class = new ReflectionClass($stub);
$method = $class->getMethod("_privateFunction");
$method->setAccessible(true);
//some assertings with $method->invoke($stub)
}
}
The test failed, because of the error:
Missing argument 1 for AbstractClass2::__construct(), called in /usr/share/php/PHPUnit/Framework/MockObject/Generator.php on line 190 and defined
AbstractClass2.php
public function __construct($param)
AbstractClass1.php
$classMock = $this->getMockForAbstractClass("AbstractClass1");
Generator.php:190
if ($callOriginalConstructor &&
!interface_exists($originalClassName, $callAutoload)) {
if (count($arguments) == 0) {
<strong>$mockObject = new $mock['mockClassName'];</strong>
} else {
$mockClass = new ReflectionClass($mock['mockClassName']);
$mockObject = $mockClass->newInstanceArgs($arguments);
}
} else ...
What do I wrong? Or how can I test my private function in this situation?
You need to pass an argument to AbstractClass1's constructor. Pass constructor arguments in an array as the second argument to getMockForAbstractClass().
$stub = $this->getMockForAbstractClass("AbstractClass1", array('param'));
Seeing as you overrode the original constructor,
public function __construct($param = array()) //Allow null $param as it would default to array();
With a new one:
public function __construct($param) //Does not allow null $param.
You will require to define the $param when you initialize the object. That's probably your problem.
Objects in PHP are not like JavaScript, they cannot be called like associative arrays. Your object initialization should look like:
$mockObject = new ClassExtendingAbstractClass1Or2('parameter');
The new keyword cannot be used in front of a variable.
Related
I am testing a class in phpunit, but I am not mocking it, the class is like that:
class MyClass extends ParentClass
{
public function doSomething($param)
{
//do some stuff
$someValue = $this->anotherMethod(); //this method is defined in the parent class
//do some other stuff with $someValue
return $finalValue;
}
}
in the test class I am doing like this
public function testDoSomething($param)
{
$myclass = new MyClass();
//here I need to control the value of $someValue, as it affects the final value returned
$res = $myClass->doSomething();
$this->assertEqual('sonething', res);
}
so my question is How can I control the value returned from anotherMethod method? I'd prefer to mock it so it does not call other methods in it
You could partially mock your class and instrument the methods that you do not want to test, as the following example:
public function testDoSomething()
{
/** #var \App\Models\MyClass $classUnderTest */
$classUnderTest = $this->getMockBuilder(\App\Models\MyClass::class)
->onlyMethods(['anotherMethod'])
->getMock();
$classUnderTest->expects($this->once())
->method('anotherMethod')
->willReturn('mocked-value');
$this->assertEquals("from-test mocked-value", $classUnderTest->doSomething("from-test"));
}
with the following sources:
ParentClass
class ParentClass
{
public function anotherMethod() {
return "parent-value";
}
}
and MyClass
class MyClass extends ParentClass
{
public function doSomething($param)
{
//do some stuff
$someValue = $this->anotherMethod(); //this method is defined in the parent class
//do some other stuff with $someValue
$finalValue = $param . ' '. $someValue;
return $finalValue;
}
}
I have downloaded an example for a payment connection. No i am trying to use it, but the constructor want's to get the interface when i declare ClassName
But i have no idea how to do that. I tried
$interface = CallbackInterface::class;
$interface = CallbackInterface();
$interface = CallbackInterface;
And many more , but i can't figure it out. Only thing i know is to implement an interface with a class. Maybe a noob question, but i've searched almost a day with no success.
$config = new Config('string1', 'string2');
$pay = new ClassName($config, $interface);
interface CallbackInterface
{
public function Key($sIdentifier, $sTransactionKey);
public function tSuccess($sTransactionKey);
}
class ClassName
{
public function __construct(Config $oConfig, CallbackInterface $oCallbacks)
{
$this->oConfig = $oConfig;
$this->oCallbacks = $oCallbacks;
}
}
you should be looking for a solution along these lines
// Create a class that implements the interface (e.g. MyClass)
// MyClass implements the interface functions: Key and tSuccess
// MyClass can now be injected as type CallbackInterface into the __construct() of class ClassName
Class MyClass implements CallbackInterface
{
public function Key($sIdentifier, $sTransactionKey)
{
// your implementation here
}
public function tSuccess($sTransactionKey)
{
// your implementation here
}
}
interface CallbackInterface
{
public function Key($sIdentifier, $sTransactionKey);
public function tSuccess($sTransactionKey);
}
class ClassName
{
public function __construct(Config $oConfig, CallbackInterface $oCallbacks)
{
$this->oConfig = $oConfig;
$this->oCallbacks = $oCallbacks;
}
}
$config = new Config('string1', 'string2');
$interface = new MyClass(); // you've now instantiated an object of type CallbackInterface
$pay = new ClassName($config, $interface);
So I have a class which is designed to "mix" other classes in, via what i call a "bridge" class. So you have your sample classes for example:
class A{
public function __construct(){}
public function hello_a(){ echo "hello A"; }
}
class B{
public function __construct(){}
public function hello_b(){ echo "hello B"; }
}
You might also have a single class called C - which needs to inherit from both A and B, but since PHP doesn't have multiple inheritance we have the following:
class C extends Bridge{
public function __construct(){
parent::__construct();
}
public function hello_C(){
$this->hello_a(); // Freaks out*
}
}
class Bridge extends AisisCore_Loader_Mixins{
public function construct(){
parent::construct();
$this->setup(array(
'A' => array(),
'B' => array()
));
}
}
And now finally we have our mix-in class which allows all of this to work. Note: this code assumes you have a auto loader using the pear naming standards to load classes for you.
class AisisCore_Loader_Mixins {
private $_classes;
private $_class_objects = array();
private $_methods = array();
public function __construct(){
$this->init();
}
public function init(){}
public function setup($class){
if(!is_array($class)){
throw new AisisCore_Loader_LoaderException('Object passed in must be of type $class_name=>$params.');
}
$this->_classes = $class;
$this->get_class_objects();
$this->get_methods();
}
public function get_class_objects(){
foreach($this->_classes as $class_name=>$params){
$object = new ReflectionClass($class_name);
$this->_class_objects[] = $object->newInstanceArgs($params);
}
}
public function get_methods(){
foreach($this->_class_objects as $class_object){
$this->_methods[] = get_class_methods($class_object);
}
return $this->_methods;
}
public function __call($name, $param = null){
foreach($this->_methods as $key=>$methods){
foreach($methods as $method){
if($name === $method){
return $this->isParam($method, $param);
}
}
}
throw new AisisCore_Loader_LoaderException("Method: " .$name.
" does not exist or it's access is not public");
}
private function isParam($method, $param){
if($param != null){
call_user_func($method, $param);
}else{
call_user_func($method);
}
}
}
You can see in class C how the class above is used, we simply just call hello_a. All is well up to this point until it tries call_user_func() and freaks out saying:
Warning: call_user_func() expects parameter 1 to be a valid callback, function 'hello_a' not found or invalid function name
Is there a particular reason it cannot find this? the classes are loaded, the methods are stored in the array, it obviously found the method in the array of methods, the method is public. whats going on?
Your call to call_user_func is passing just the method name, so it's looking for a global function. You must pass your class name or instance:
$a = new A(); // Or however you plan to get your instance of A
call_user_func(array($a, $method));
I have a class Block_Model (actually a model in Kohana framework) with 2 methods input()and output().
class Block_Model extends ORM {
function input($arg) {
//...
}
function output() {
//...
}
//...
}
The method input is called from a function written inside a controller called Home_Controller and it passes an argument to the method input.
class Home_Controller extends Controller {
function doSomething() {
$block = new Block_Model();
//...
$block->input($val);
//...
}
}
How can I make the argument passed to input() be accessible in the method output()?
You'll need private property:
class Something{
private $_variable = "";
function input( $data ){
$this->_variable = $data;
//do the rest of function
}
function output( ){
//get previously set data
echo $this->_variable;
}
}
This is similar to #silent's answer, but you can combine setter & getter in one method.
protected $_foo;
public function foo($val = NULL)
{
if ($val === NULL)
{
// its a getter!
return $this->_foo;
}
// its a setter
$this->_foo = $val;
// return current object, so it becomes a chainable method
return $this;
}
Now you can use $value = $object->foo(); and $object->foo($value)->do_something_else();
Suppose we have a class. We create an object from the class and when we do the class Extends himself base on the object initialization value..
For example:
$objectType1 = new Types(1);
$objectType1->Activate(); // It calls an activation function for type 1
$objectType2 = new Types(2);
$objectType2->Activate(); // It calls an activation function for type 2
I don't want to use the standard procedure of class extending:
class type1 extends types{}
You cannot extend a class at runtime. Use an instance variable to distinct the two type or use a factory.
Example for instance variable:
class Types() {
private $type;
public function __construct($type) {
$this->type = $type;
}
public function activate() {
if($this->$type == 1) {
// do this
}
else if($this->type == 2) {
// do that
}
}
}
Example for factory pattern:
abstract class BaseClass {
// Force Extending class to define this method
abstract public function activate();
// Common method
public function printOut() {
echo "Hello World";
}
}
class Type1 extends BaseClass {
public function activate() {
// do something
}
}
class Type2 extends BaseClass {
public function activate() {
// do something else
}
}
class TypeFactory {
public static function getType($tpye) {
if($type == 1) {
return new Type1();
}
else if($type == 2) {
return new Type2();
}
}
}
then you do:
$obj = TypeFactory::getType($1);
$obj->activate();
Update:
Since PHP 5.3 you can use anonymous functions. Maybe you can make use of this.