I am learnig about test doubles, but I'm a little confused about the difference between Mocks and Stubs.
So, looking for some references about this topic, I found this article (is a good article, but some examples are a little bit confusing)
Then, I was following the examples in my IDE, however, the examples about Mocks and Stubs are the same.
Stubs example:
class A
{
public function calculate() { }
}
class B
{
private A $a;
public $value;
public function __construct(A $a)
{
$this->a = $a;
}
public function setValue()
{
$this->value = $this->a->calculate();
}
}
class StubsTest extends \PHPUnit\Framework\TestCase
{
public function testValueCorrect()
{
$a = $this->createStub(A::class);
$a
->method('calculate')
->willReturn(10);
$b = new B($a);
$b->setValue();
$this->assertEquals(10, $b->value);
}
}
Mock example:
use PHPUnit\Framework\TestCase;
class A {
public function calculate() { }
public function doSomething() { }
}
class B {
private $a;
public $value;
public function __construct(A $a)
{
$this->a = $a;
}
public function setValue()
{
$this->value = $this->a->calculate();
}
public function doSomethingWithA()
{
$this->a->doSomething();
}
}
class MockTest extends TestCase
{
public function testValueCorrect()
{
$a = $this->createStub(A::class);
$a
->method('calculate')
->willReturn(10);
$b = new B($a);
$b->setValue();
$this->assertEquals(10, $b->value);
}
}
What the real difference between both? These code examples above are valid?
Related
I would like to bind a function (a method of a class) to another class. Any idea of how i could achieve this?
Here is an example of what i want:
class A {
protected $prop = "prop A";
function method($arg1, ...) {
return $this->prop;
}
}
class B {
protected $prop = "prop B";
// need help here
}
So i want to "bind" the method "method" of class "A" to class "B" so it'll be possible to do $b = new B(); $b->method($arg1, ...); and obtain "prop B";
Thanks in advance!!
I tried:
class B {
protected $instance;
protected $prop = "prop B";
public function __construct($instance) {
$this->instance = $instance;
}
public function __call($method, $args) {
return call_user_func_array([$this->instance, $method], $args);
}
}
$b = new B(new A());
$b->method();
But still outputing "prop A";
I tried this too:
class B {
protected $prop = "prop B";
public function __call($method, $args) {
return call_user_func_array(Closure::bind($this->$method, $this, get_called_class()), $arguments);
}
}
$a = new A();
$b = new B();
$b->method = $a->method;
$b->method();
But i'm getting this error: Closure::bind() expects parameter 1 to be Closure, null given....
At last i tried this too:
class B {
protected $instance;
protected $prop = "prop B";
public function __construct($instance) {
$this->instance = $instance;
}
public function __call($method, $args) {
$new_method = $this->instance->$method->bindTo($this);
return call_user_func_array($new_method, $args);
}
}
$b = new B(new A());
$b->method();
Here, an error says $this->instance->$method is null
Using dependency injection, you can access the passed object:
class A {
public function speak() { return ‘meow’; }
}
class B {
public function __construct($obj) {
$this->a = $obj;
}
}
$demo = new B( new A));
print $demo->a->speak();
// => meow
From here, if you want B->speak to refer to A->speak:
class A {
public function speak() { return ‘meow’; }
}
class B {
public function __construct($obj) {
$this->a = $obj;
}
public function speak() {
return $this->a->speak();
}
}
$demo = new B( new A));
print $demo->a->speak(); // meow
print $demo->speak(); // meow
Or, if B is a special kind of A:
// use obj A above
class B extends A {
// do stuff B knows
}
$demo = new B;
print $demo->speak();
// => meow
If you’re wanting the exact same method in both classes, perhaps what you’re looking for is traits. Traits are more or less an include for objects which lets objects “share” code. (Personally I think it’s a fancy way of violating DRY and is better handled with DI... but smarter people than I have included it in the language)
Using traits would be something like this (double check docs, I’ve never used this)
trait sharedMethod {
public function speak() {
return $this->prop;
}
}
class A {
use sharedMethod;
public $prop = “I’m from A”;
}
class B {
use sharedMethod;
public $prop = “I’m from B”;
public function __construct(object $a) {
$this->a = $a;
}
/**
* use this to get A’s method, or omit to keep B’s method
*/
public function speak() {
return $this->a->speak();
}
}
$demo = new B( new A));
print $demo->speak(); // I’m from A
// if you don’t override the method
$demo = new B( new A));
print $demo->speak(); // I’m from B
I'm learning php unit test. I've question; how to set property value from method? Here is my example code:
class Variables
{
public $date;
public function setDate(\DateTime $date) {
$this->date = $date;
}
}
class Process
{
public function process(Variables $var) {
if ($var->date->getTimeStamp() > 0) {
return 'success';
}
return 'failed';
}
}
class ProcessTest extends PHPUnit_Framework_TestCase
{
public function testProcess()
{
$mock = \Mockery::mock('Variables');
$mock->date = new \DateTime();
$procy = new Process();
$actual = $procy->process($mock);
$this->assertEquals('success', $actual);
}
}
Like in the codes above, I know, I can set property date by:
$mock->date = new \DateTime();
because it's public.
What if the property date is private or protected? How to set from mockery? I tried to do something like this, but got an error.
$mock->shouldReceive('setDate')->once()->andSet('date', new \DateTime());
Sample class that describes my question :
class Calculation {
protected $a;
protected $b;
protected $c;
public function __construct() {
;
}
public function setA($a) {
$this->a = $a;
}
public function setB($b) {
$this->b = $b;
}
public function call() {
$this->c = (int) $this->a + (int) $this->b;
}
public function getC() {
return $this->c;
}
}
I need your advice.
You would probably add an accessor to Variables, use it in Process::process() instead of accessing the public property, and thus, you would have to set up an expectation that the accessor is called when you invoke Process::process():
$date = new \DateTime();
$variables = \Mockery::mock('Variables');
$variables->shouldReceive('getDate')->withNoArgs()->andReturn($date);
$process = new Process();
$this->assertSame('success', $process->process($variables));
For reference, see:
http://docs.mockery.io/en/latest/reference/expectations.html
I have 3 classes:
Class A - Parent Class
Class B - Child Class
Class C - Class to be used in Class A
I want to use functions from class C using variables from my Child class.
<?php
class A
{
public function __construct()
{
$this->load();
}
public function load()
{
$class = new C();
$class->test = $this->test;
$this->c = $class;
}
}
class B extends A
{
public function __construct()
{
parent::__construct();
}
}
class C
{
public function display()
{
echo $this->test;
}
}
$b = new B();
$b->test = 1;
$b->c->display();
Your problem is here:
$class->test = $this->test;
You are attempting to use a property that is not yet defined, because when you do this:
$b->test = 1;
the constructor has already been called, and there's nothing in your classes to update C with the value of B's test property.
You can solve this in a couple of different ways.
1) Send the value in B's constructor, and pass it down the entire chain:
class A
{
public function __construct($test)
{
$this->load($test);
}
public function load($test)
{
$class = new C();
$class->test = $test;
$this->c = $class;
}
}
class B extends A
{
public function __construct($test)
{
parent::__construct($test);
}
}
class C
{
public function display()
{
echo $this->test;
}
}
$b = new B(123);
$b->c->display();
2) Add a method to B that will update C's property:
<?php
class A
{
public function __construct()
{
$this->load();
}
public function load()
{
$class = new C();
$this->c = $class;
}
}
class B extends A
{
public function __construct()
{
parent::__construct();
}
public function setTest($test)
{
$this->c->test = $test;
}
}
class C
{
public function display()
{
echo $this->test;
}
}
$b = new B();
$b->setTest(123);
$b->c->display();
Or perhaps a combination of both.
I'm not sure with my approach. A have two classes and call functions of first class in second class like this:
class A {
public function aClassFunction() {...}
}
class B {
private $aClass;
public function __construct() {
$this->aClass = new A();
}
public function bClassFunction() {
$test = $this->aClass->aClassFunction();
}
}
It just works, but looks "suspiciously".
You can use dependency injection in B class. That approach helps you mocking classes in test.
class B {
private $aClass;
public function __construct(A $a) {
$this->aClass = $a;
}
public function bClassFunction() {
$test = $this->aClass->aClassFunction();
}
}
$b = new B(new A());
Looks "suspiciously" like a dependency. Why not Inject the Dependency?
class B {
private $aClass;
public function __construct($object) {
$this->aClass = $object;
}
public function bClassFunction() {
$test = $this->aClass->aClassFunction();
}
}
While cloning an object, I need to perform the same initializations that happen during the object construction.
Can I do this?
public class MyClass {
protected $myVar;
public function __construct()
{
$this->myVar = 0
}
public function __clone()
{
$this->__construct();
}
}
You can do that just fine
class MyClass {
protected $myVar;
public function __construct()
{
echo "constructing!\n";
$this->myVar = 0;
}
public function __clone()
{
echo "cloning!\n";
$this->__construct();
}
}
$a = new MyClass();
$b = clone $a;
Output
constructing!
cloning!
constructing!