How to mock a non-deterministic method? - php

Consider class with non-deterministic method:
class Foo
{
public function getSome(): int
{
static $int = 0;
return ++$int;
}
}
How can I mock same behaviour using Mockery?
class Bar
{
public function useFoo(Foo $foo)
{
echo $foo->getSome() . ", ";
echo $foo->getSome() . ", ";
echo $foo->getSome();
}
}
$mock = Mockery::mock(Foo::class);
$mock->shouldReceive('getSome')->andReturn(1);
$mock->shouldReceive('getSome')->andReturn(2);
$mock->shouldReceive('getSome')->andReturn(3);
// Should display 1, 2, 3
$bar = new Bar();
$bar->useFoo($mock);
Another example:
class Clock
{
public function getTime(): int
{
return time();
}
}
class Sleeper
{
public function sleep(int $seconds): void
{
sleep($seconds);
}
}
How can I mock same behaviour using Mockery?
class Stoper
{
private Clock $clock;
private Sleeper $sleeper;
public function __construct(Clock $clock, Sleeper $sleeper)
{
$this->clock = $clock;
$this->sleeper = $sleeper;
}
public function measure(int $seconds): int
{
$start = $this->clock->getTime();
$this->sleeper->sleep($seconds);
return $this->clock->getTime();
}
}
$clockMock = Mockery::mock(Clock::class);
$clockMock->shouldReceive('getTime')->andReturn(1000000001);
$clockMock->shouldReceive('getTime')->andReturn(1000000011);
$sleeperMock = Mockery::mock(Sleeper::class);
$sleeperMock->shouldReceive('sleep');
$stoper = new Stoper($clockMock, $sleeperMock);
$this->assertEquals(10, $stoper->measure(10));
Dirty solution
I figured out I can make dirty trick
class ClockCaller
{
public function getTime(int $callNumber)
{
return (new Clock())->getTime();
}
}
class Stoper
{
private ClockCaller $clockCaller;
private Sleeper $sleeper;
public function __construct(ClockCaller $clockCaller, Sleeper $sleeper)
{
$this->clockCaller = $clockCaller;
$this->sleeper = $sleeper;
}
public function measure(int $seconds): int
{
$start = $this->clockCaller->getTime(1);
$this->sleeper->sleep($seconds);
return $this->clockCaller->getTime(2);
}
}
$clockMock = Mockery::mock(ClockCaller::class);
$clockMock->shouldReceive('getTime')->withArgs([1])->andReturn(1000000001);
$clockMock->shouldReceive('getTime')->withArgs([2])->andReturn(1000000011);
But it seems very nasty

The answer to your question is given by the Simple Example in the Mockery documentation. A temperature service that should return successively 10, 12 and 14 degrees can be mocked like this:
$service = Mockery::mock('service');
$service->shouldReceive('readTemp')
->times(3)
->andReturn(10, 12, 14);
So, for your Foo class:
$mock = Mockery::mock(Foo::class);
$mock->shouldReceive('getSome')
->times(3)
->andReturn(1, 2, 3);
and for your Clock:
$clockMock = Mockery::mock(Clock::class);
$clockMock->shouldReceive('getTime')
->times(2)
->andReturn(1000000001, 1000000011);

you can check the output with expectOutputString:
public function testFoo(){
$this->expectOutputString("foo");
echo "foo";
}
about sleep function, you can use \sleep() (global namespase), and override it..
also, you can use mocking built-in PHP functions

Related

Convert Class Instance to float

Lets say there is this class:
class Number {
private $asString;
private $asFloat;
public function __construct($input) {
$this->asString = $input;
$this->asFloat = $this->parse($input);
}
private function parse($input) {…}
//magic method for $n1 . $n2 operations
public function __toString() { … }
//method for $n1 + $n2 operations
public function __toFloat() { … }
}
Unfortunately the __toFloat() magic method does not exist. Is there any way, other than: $sum = $n1->toFloat() + $n2->toFloat(), without having to call that ->toFloat() method all the time, when the object is used in the context of mathematical operations.
In Javascript on has the ability to create a valueOf() method and I am searching for a way to create something similar in php. Any ideas?
You can use invoke as solution for this case
<?php
class Number
{
private $asString;
private $asFloat;
public function __construct($input)
{
$this->asString = $input;
$this->asFloat = $this->parse($input);
}
public function __invoke()
{
return $this->asFloat;
}
private function parse($input)
{
return (float) $input;
}
public function __toString()
{
return $this->asString;
}
}
$n1 = new Number(5);
$n2 = new Number(3);
var_dump($n1() + $n2());

php unit test mockery set property from method

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

Switch visibility in php if parameter

I'm wondering if its possible to switch the visibility in PHP. Let me demonstrate:
class One {
function __construct($id){
if(is_numeric($id)){
//Test function becomes public instead of private.
}
}
private function test(){
//This is a private function but if $id is numeric this is a public function
}
}
Is such thing even possible?
I would use an abstract class with two implementing classes: One for numeric and one for non-numeric:
abstract class One {
static function generate($id) {
return is_numeric($id) ? new OneNumeric($id) : new OneNonNumeric($id);
}
private function __construct($id) {
$this->id = $id;
}
}
class OneNumeric extends One {
private function test() {
}
}
class OneNonNumeric extends One {
public function test() {
}
}
$numeric = One::generate(5);
$non_numeric = One::generate('not a number');
$non_numeric->test(); //works
$numeric->test(); //fatal error
It can be faked up to a point with magic methods:
<?php
class One {
private $test_is_public = false;
function __construct($id){
if(is_numeric($id)){
$this->test_is_public = true;
}
}
private function test(){
echo "test() was called\n";
}
public function __call($name, $arguments){
if( $name=='test' && $this->test_is_public ){
return $this->test();
}else{
throw new LogicException("Method $name() does not exist or is not public\n");
}
}
}
echo "Test should be public:\n";
$numeric = new One('123e20');
$numeric->test();
echo "Test should be private:\n";
$non_numeric = new One('foo');
$non_numeric->test();
I haven't thought about the side effects. Probably, it's only useful as mere proof of concept.

Combinig a static class and a non-static one

For example consider class X that has some utility methods ('foo','bar') that do some operation on some property of X. These method are also useful for other external variable.
Some may implement X and staticX classes as below:class Foo
class StaticX
{
public static function foo($p)
{
return $p * $p;
}
}
class X
{
private $p=4;
public function foo()
{
return StaticX::foo($this->p);
}
}
$x= new x;
echo $x->foo();
echo StaticX::foo(3);
But this approach has some maintainability issues.
Is there any better solution?
class X
{
private $p;
public function foo()
{
return self::doFoo($this->p);
}
public static function doFoo($p)
{
return $p * $p;
}
}
I like foolishSeths answer, but what about this?
class X
{
private static $p;
public static function foo($p=null)
{
if ( is_null( $p ) ) {
$p = self::$p;
}
return $p * $p;
}
}
Since PHP 5.4 you can make use of traits. See http://www.php.net/manual/en/language.oop5.traits.php
trait fooBehavior {
function getFoo() { return self::foo($this->p); }
static function foo($p) { return $p * $p; }
}
class X {
use fooBehavior;
private $p;
public function __construct($p) { $this->p = $p; }
}
$x = new X(2);
echo $x->getFoo(); // echoes 4
echo $x::foo(2); // echoes 4

PHP mandatory function call

I understand that one can use interfaces to mandate the definition of a function, but I cannot find something that enables one to mandate function calls, such that e.g. if I create a class being a member of another class (via extends, etc), with a function, for that class to automatically ensure that mandatory functions are called in part with that function.
I mean, to clarify further:
class domain {
function isEmpty($input) {
//apply conditional logic and results
}
}
class test extends domain {
function addTestToDBTable($test) {
/**
* try to add but this class automatically makes it so that all rules of
* class domain must be passed before it can run
* - so essentially, I am no longer required to call those tests for each and
* every method
**/
}
}
Apologies if this appears incoherent by any means. Sure, it seems lazy but I want to be able to force context without having to concern abou
Update:
Okay, to clarify further: in PHP, if I extend and declare a __construct() for a child class, that child class will override the parent __construct(). I do not want this, I want the parent construct to remain and mandate whatever as it pleases just as the child class may do so also.
I guess it can be done in two different ways.
Aspect Oriented Programming
Have a look here https://github.com/AOP-PHP/AOP
Generate or write Proxy classes
A really simple example could be:
<?php
class A {
public function callMe() {
echo __METHOD__ . "\n";
}
}
class B extends A {
// prevents instantiation
public function __construct() {
}
public function shouldCallMe() {
echo __METHOD__ . "\n";
}
public static function newInstance() {
return new ABProxy();
}
}
class ABProxy {
private $b;
public function __construct() {
$this->b = new B();
}
public function __call($method, $args) {
$this->b->callMe();
return call_user_func_array(array($this->b, $method), $args);
}
}
// make the call
$b = B::newInstance();
$b->shouldCallMe();
// Outputs
// ------------------
// A::callMe
// B::shouldCallMe
Hopes this helps a bit.
Sounds like you want a Decorator.
See This answer for a detailed explanation on how to do it. Note that it does not require a class extension.
I would use a domain-validating decorator with some doc-block metaprogramming magic. But this is really a job for an entire library, which no doubt exists.
fiddle
<?php
class FooDomain {
public static function is_not_empty($input) {
return !empty($input);
}
}
class Foo {
/**
* #domain FooDomain::is_not_empty my_string
*/
public function print_string($my_string) {
echo $my_string . PHP_EOL;
}
}
$foo = new DomainValidator(new Foo());
$foo->print_string('Hello, world!');
try {
$foo->print_string(''); // throws a DomainException
} catch (\DomainException $e) {
echo 'Could not print an empty string...' . PHP_EOL;
}
// ---
class DomainValidator {
const DOMAIN_TAG = '#domain';
private $object;
public function __construct($object) {
$this->object = $object;
}
public function __call($function, $arguments) {
if (!$this->verify_domain($function, $arguments)) {
throw new \DomainException('Bad domain!');
}
return call_user_func_array(
array($this->object, $function),
$arguments
);
}
public function __get($name) {
return $this->object->name;
}
public function __set($name, $value) {
$this->object->name = $value;
}
private function verify_domain($function, $arguments) {
// Get reference to method
$method = new \ReflectionMethod($this->object, $function);
$domains = $this->get_domains($method->getDocComment());
$arguments = $this->parse_arguments(
$method->getParameters(),
$arguments
);
foreach ($domains as $domain) {
if (!call_user_func(
$domain['name'],
$arguments[$domain['parameter']]
)) {
return false;
}
}
return true;
}
private function get_domains($doc_block) {
$lines = explode("\n", $doc_block);
$domains = array();
$domain_tag = DomainValidator::DOMAIN_TAG . ' ';
foreach ($lines as $line) {
$has_domain = stristr($line, $domain_tag) !== false;
if ($has_domain) {
$domain_info = explode($domain_tag, $line);
$domain_info = explode(' ', $domain_info[1]);
$domains[] = array(
'name' => $domain_info[0],
'parameter' => $domain_info[1],
);
}
}
return $domains;
}
private function parse_arguments($parameters, $values) {
$ret = array();
for ($i = 0, $size = sizeof($values); $i < $size; $i++) {
$ret[$parameters[$i]->name] = $values[$i];
}
return $ret;
}
}
Output:
Hello, world!
Could not print an empty string...

Categories