PHPUnit simple method to compare two dates - php

I'm not familiar with PHPUnit and just want to execute a simple method that return a DateTimeImmutable and compare it with another DateTimeImmutable.
public function testGetLunchTimesBeginHour()
{
$minLunchTime = \DateTimeImmutable::createFromFormat('H:i', self::MIN_BEGIN_LUNCH);
$maxLunchTime = \DateTimeImmutable::createFromFormat('H:i', self::MAX_END_LUNCH);
foreach($this->daysOfAppointments as $dayOfAppointments){
$appointments = $this->makeAppointmentsDatetime($dayOfAppointments);
$mock = $this->getMockBuilder(GetLunchTimesBeginHour::class)->getMock();
$expected = \DateTimeImmutable::createFromFormat('H:i', $dayOfAppointments['expected']);
$actualResult = $mock->expects($this->once())->method('getLunch')->with($appointments, $minLunchTime, $maxLunchTime, self::DURATION_LUNCH);
$this->assertEquals(
$expected,
$actualResult,
"unexpected result");
}
}
I understand the problem is that $actualResult is a PHPUnit\Framework\MockObject\Builder\InvocationMocker instead of a DateTimeImmutable.
How to just execute the method and get the result ? Must I call the real class instead of a mock ?

Well, I'm not sure what you want to test here, but here an example:
$expectedDate = new \DateTimeImmutable('2022-11-01');
$now = new \DateTimeImmutable('2022-10-01');
$myClass = new MyClass();
$newDate = $myClass->calcul($now);
// ± 5 seconds are equals
self::assertEqualsWithDelta($expectedDate->getTimestamp(), $newDate->getTimestamp(), 5);
And use a foreach in a unit-test is not a good practice, it's better to use a dataProvider
https://phpunit.readthedocs.io/en/9.5/writing-tests-for-phpunit.html#writing-tests-for-phpunit-data-providers

I would not mock what you are trying to test. Try something like this, you will need to fill in the gaps and mock any dependancies though. This is assuming that the method getLunch returns a dateTime object.
public function testGetLunchTimesBeginHour()
{
$minLunchTime = \DateTimeImmutable::createFromFormat('H:i', self::MIN_BEGIN_LUNCH);
$maxLunchTime = \DateTimeImmutable::createFromFormat('H:i', self::MAX_END_LUNCH);
//Class you are testing, you will need to mock the class dependancies and pass them in.
$testObject = new GetLunchTimesBeginHour();
foreach($this->daysOfAppointments as $dayOfAppointments){
$appointments = $this->makeAppointmentsDatetime($dayOfAppointments);
$mock = $this->getMockBuilder(GetLunchTimesBeginHour::class)->getMock();
$expected = \DateTimeImmutable::createFromFormat('H:i', $dayOfAppointments['expected']);
//get real response from created object you are testing.
$actualResult = $testObject->getLunch($appointments, $minLunchTime, $maxLunchTime, self::DURATION_LUNCH);
$this->assertEquals(
$expected,
$actualResult,
"unexpected result"
);
}
}

Related

PHP: Can attribute values in one class be transferred to attributes in another class with the same name?

I am accidentally sort of making my own framework. (Before you start pls see PS at end!)
So for example I have:
class MessageSchedule
{
use Utility;
protected $messageScheduleID;
protected $messageScheduleName;
...
protected $minDaysPerWeek = 7;
protected $maxDaysPerWeek = 3;
protected $currentTimeZone = "Pacific/Honolulu";
}
class MessageSendList
{
use Utility;
protected $messageSendListID = NULL;
protected $messageScheduleID = NULL;
protected $messageScheduleName = NULL;
...
protected $currentTimeZone = NULL;
}
All tables in the database have mirrored classes with EXACTLY the same names for variables and attributes. Now in my Utility traits I have functions to do CRUD and to read an instantiated object into a script as a key/value array which I can then extract.
Using the calculated attribute names of any class I can do a quick (and very dirty) population of an an object with a foreach loop implementing sort of...
$thisThing = new Thing;
$thisThing -> setThisValue = $thisValue;
... etc
I can do it that way BUT it would be less error prone if I could just sort of "clone if attributes there" function in PHP. Sort of:
$thisFoo = new Foo;
$thisBar = new Bar;
$thisOne = $thisFoo->doCreat(12); //Instantiates Foo with values from FooID=12
$thisBar = partialClone($thisOne); // If PHP had a partial clone! That is what I am looking for.
In my case it would copy over the values of $messageScheduleID $messageScheduleName and $timeZone into a new instance of MessageSendList.
Hope that is vaguely comprehensible.
Thanks. Steve
... Later: This is what I am currently using and trying to replace.
$thisSendList = new MessageSendList();
$vars = $thisSendList->classAttributes();//Generic find attribute name
foreach ($vars as $var)
{
if (isset($$var))
{
$thisSendList -> doSet($var, $$var); // Generic set attributes
}
}
$thisSendList -> doCreate();
PS OK I KNOW that I SHOULD be using Laravel or some such but the learning curve there is pretty steep. I already have to use jQuery, PHP, SQL, HTML, CSS, phpStorm (almost a programming language in itself) etc. etc.
I am a pretty good (very slow) programmer and I can code anything and once I have done it I understand it. One day I will probably move to Laravel but, for the moment, it is a step too far for me ... and I know someome will still say "Why not use Yi" or whatever. I am 64, no memory (far too much cannabis) so...
You can use ReflectionClass to get array of public properties, than use array_intersect of those arrays to get list of properties that you should copy.
function getPropertyList($object)
{
$reflection = new ReflectionClass($object);
$properties = $reflection->getProperties(ReflectionProperty::IS_PUBLIC);
$result = [];
foreach ($properties as $property) {
$result[] = $property->getName();
}
return $result;
}
$thisFoo = new Foo;
$thisBar = new Bar;
$thisOne = $thisFoo->doCreat(12); //Instantiates Foo with values from FooID=12
$propsFoo = getPropertyList($thisOne);
$propsBar = getPropertyList($thisBar);
$common = array_intersect($propsFoo, $propsBar);
foreach ($common as $property) {
$thisBar->$property = $thisOne->$property;
}
You can turn this functionality into trait that you would use in classes where you need it.
Resources:
ReflectionClass::getProperties()
array_intersect()

Testing a simple function in a model with Mockery

I'm totally new with Mockery which is embedded in Laravel. I have the pain to test a simple model function which increments a portion of a reference, whatever the value I'm passing to test the result is ok even when it should fail.
I think I made an error somewhere or I don't understand the documentation.
Thanks for your help.
Here is the simple function to test
public function incrementRefFormation(string $value):string
{
$split = str_split($value);
$group1 = '';
for ($i=0;$i<11;$i++) {
$group1 .= $split[$i];
}
$group2 = $split[11].$split[12];
$group2 = (int)$group2;
$group2++;
return $group1.$group2.$split[13];
}
Here is the test which should fail
public function testIncrementRefFormation()
{
//$testValue = '1 332 8100 20S';
$testValue = '123456';
$expectedValue = '1332810021S';
$mock = Mockery::mock('App\Models\Formation');
$mock->shouldReceive(['incrementRefFormation' => $expectedValue])
->once();
var_dump($mock->incrementRefFormation($testValue));
}
Many thanks!
Mockery is used for creating 'mocks', which are dumb objects doing only what you tell them to do (e.g. method x will return y if supplied with parameter z). Usually it's used for mocking dependencies of the class you want to test. In your case you probably won't need it.
So your test would probably look something like this
$formation = new App\Models\Formation();
$actualvalue = $formation->incrementRefFormation($testValue);
$this->assertEquals($expectedValue, $actualvalue);

PHP: Problems using Singleton pattern and understanding __clone method

I am trying to implement the singleton pattern in php like described here in Example #2:
http://www.php.net/singleton
When I run the example code
$singleton = Example::singleton(); // prints "Creating new instance."
echo $singleton->increment(); // 0
echo $singleton->increment(); // 1
$singleton = Example::singleton(); // reuses existing instance now
echo $singleton->increment(); // 2
echo $singleton->increment(); // 3
it allways ends with Fatal Error 'Clone is not allowed.' directly after 'Creating new instance.'
I would expect that there is no reason for php to call the __clone-method.
In another real-life project of mine I want to have a singleton PlayerManager that holds Player-objects in an array (loaded only once in __construct) and has functions like GetPlayers() or GetPlayersByID($id).
In my script I write something like
$pm = PlayerManager::GetInstance();
$p1 = $pm->GetPlayerByID(0);
echo $p1->SomeNumber; //100
$p1->SomeNumber = 200;
$p2 = $pm->GetPlayerByID(0);
echo $p2->SomeNumber; //100 and not 200, as I would expect
Can someome give me some hints how to implement the PlayerManager using the Singleton pattern correct? I'm not sure if it is only a problem with the singleton or also a problem with returning object references...
I'm not quiet sure why you're getting that error (post your singleton class if you want help with that). Though I always preferred this version to the one you're using, it's a bit simpler: http://www.talkphp.com/advanced-php-programming/1304-how-use-singleton-design-pattern.html
So with the above, your code would look like:
class Counter
{
$CurrentValue = 0;
// Store the single instance of Database
private static $m_pInstance;
private function __construct() { }
public static function getInstance()
{
if (!self::$m_pInstance)
{
self::$m_pInstance = new Counter();
}
return self::$m_pInstance;
}
public function increment ($by)
{
$this->CurrentValue += $by;
return $this->CurrentValue;
}
public function getValue ()
{
return $this->CurrentValue;
}
}
And to use:
$counter = Counter::getInstance();
echo $counter->increment(); // 0
echo $counter->increment(); // 1
$counter = null;
$counter = Counter::getInstance(); // reuses existing instance now
echo $counter->increment(); // 2
echo $counter->increment(); // 3
Tell me how that works out for you.

How to Initialize a Instance of a PHP Class using another Object Instance?

What would be a good way (along with any pros and cons) of initializing an instance of a PHP class with another object of the same class (ideally in PHP 4.x)?
Here in initialize() is essentially what I'd like to be able to do (example is extremely simplified from my use-case, see below):
$product = new Product('Widget');
$product2 = new Product('Widget #2');
$product->initialize($product2);
echo $product->name; // echos "Widget #2"
class Product {
var $name;
function __constructor($name) {
$this->name = $name;
}
function initialize($product) {
// I know this cannot be done this way in PHP.
// What are the alternatives and their pros & cons?
$this = $product;
}
}
I know this may not be "good programming practice"; with 20+ years programming experience on other languages I know a bit about what's good and what's not. So hopefully we won't get hung up on if doing this makes sense or not. I have a use-case working with some open-source code that I can't change so please just bear with me on my need for it. I'm actually trying to create an OOP wrapper around some really ugly array code buried deep in the core of WordPress.
I'm trying to write it so in future versions they can move away from the ugly array-based code because everyone will be using the new API that otherwise fully encapsulated these nasty arrays. But to make it work elegantly I need to be able to do the above (in PHP 4.x) and I don't want to write code that just copies the properties.
Thanks in advance for your help.
UPDATE
Many of you are suggesting clone but unless I misunderstand that doesn't address the question. clone makes a copy; that's not the crux of the question. I'm instead trying to get the constructed object to "become" the object passed in. At this point I'm assuming there isn't a way to do that based on the fact that 0 out of 5 answers have suggested anything but I'll wait a bit longer before selecting a best in case it was simply that my questions was unclear.
In PHP 5, object cloning might be more relevant:
http://php.net/manual/en/language.oop5.cloning.php
You can define a special __clone method.
In PHP 4 and 5, you can copy properties via:
function copy($obj)
{
foreach (get_object_vars($obj) as $key => $val)
{
$this->$key = $val;
}
}
However, you wrote "I don't want to write code that just copies the properties," and I'm not exactly sure what you mean by that.
Preferred way of doing this is to use clone keyword and to implement appropriate __clone() method if needed as mentioned by other posters. Another trick way of doing this (con: slow, pros: can be stored, sent over network and works identical in php4/5) is to serialize an object and then unserialize to create new copies of it with identical variable values.
Example:
$productCopy = unserialize(serialize($product));
EDIT: Sorry, misunderstood what you were asking for. You will have to initialize variables of the object being constructed with passed in object's variables inside of the constructor. You can't return a reference to another object from the constructor.
Example:
public function __construct($name, $object = null) {
if($object) {
foreach(get_object_vars($object) as $k => $v) {
$this->$k = $v;
}
} else {
$this->name = $name;
}
}
class Product {
var $name;
function __construct($value) {
if (is_a($value, 'Product')) {
$this->name = $value->name;
} else {
$this->name = $value;
}
}
}
Similarly, you can use instanceof instead of is_a if you prefer (depending on your PHP version).
Now you can pass a Product instance OR a name to the construct.
$product = new Product('Something');
$clone = new Product($product);
This is the best way of doing it so far:
http://www.blrf.net/howto/51_PHP__How_to_control_object_instances_in_PHP_.html
Don't use "new", instead use a static function that returns the instance you want.
I have done the following:
class MyClass {
private static $_instances;
public static function get($id) {
if (!self::$_instances) self::$_instances = Array();
$class = get_called_class();
if (!array_key_exists($class, self::$_instances)) self::$_instances[$class] = Array();
if (!is_numeric($id) || $id == '') throw new Exception('Cannot instantiate a non-numeric ID.');
if (array_key_exists($id, self::$_instances[$class])) return self::$_instances[$class][$id];
else {
self::$_instances[$class][$id] = new static($id);
return self::$_instances[$class][$id];
}
}
function __construct($id=false) {
// new instance code...
// I use $id=false to create new a db table row not load an old one
}
}
Usage:
// New instance
$a = new MyClass();
$a = new MyClass;
// MyClass with $id = 1
$b = MyClass::get(1);
$c = MyClass::get(1);
$d = new MyClass(1);
$b and $c point to the same object, while $d is a new one.
Caveats:
Garbage collection will no longer apply as your instances are stored in a static array
You'll have to change your code to use MyClass::get
Notes in my code:
New instances are called with "new static" instead of "new self" to use late static bindings.
You can set your constructor to private. This will break all your old code if you use "new", but will ensure you don't get double instances or more. You'll have to change a bit in the get function's arguments and code to allow $id=false or $id=-1 or whatever.
maybe
$product = new Product('Widget');
$product2 = new Product(null, $product);
echo $product2->name; // echos "Widget #2"
class Product {
var $name;
function __constructor($name, $product = null) {
$this->name = !empty($name) ? $name : $product->name;
}
}
Adding another answer due to it being radically different.
$product = new Product('Widget');
$product2 = new Product('Widget #2');
$product =& $product2;
echo $product->name; // echos "Widget #2"
That should work.

how do I override php://input when doing unit tests

I'm trying to write a unit test for a controller using Zend and PHPUnit
In the code I get data from php://input
$req = new Zend_Controller_Request_Http();
$data = $req->getRawBody();
My code works fine when I test the real application, but unless I can supply data as a raw http post, $data will always be blank. The getRawBody() method basically calls file_get_contents('php://input'), but how do I override this in order to supply the test data to my application.
I had the same problem and the way I fixed it was to have the 'php://input' string as a variable that is settable at run time. I know this does not really apply directly to this question as it would require modifying the Zend Framework. But all the same it may be helpful to someone.
For example:
<?php
class Foo {
public function read() {
return file_get_contents('php://input');
}
}
would become
<?php
class Foo {
public $_fileIn = 'php://input';
public function read() {
return file_get_contents($this->_fileIn);
}
}
Then in my unit test I can do:
<?php
$obj = new Foo();
$obj->_fileIn = 'my_input_data.dat';
assertTrue('foo=bar', $obj->read());
You could try mocking the object in your unit tests. Something like this:
$req = $this->getMock('Zend_Controller_Request_Http', array('getRawBody'));
$req->method('getRawBody')
->will($this->returnValue('raw_post_data_to_return'));
Provided the $req->getRawBody() is, as you say, the same as file_get_contents('php://input')...
$test = true; /* Set to TRUE when using Unit Tests */
$req = new Zend_Controller_Request_Http();
if( $test )
$data = file_get_contents( 'testfile.txt' );
else
$data = $req->getRawBody();
Not a perfect solution, but similar to what I have used in the past when designing scripts to handle piped emails with great success.
Zend_Controller_Request_HttpTestCase contains methods for setting and getting various http request/responses.
For example:
$req = new Zend_Controller_Request_HttpTestCase;
$req->setCookie('cookie', 'TRUE');
$test = $this->controller->cookieAction($req);
$this->assertSame($test, TRUE);

Categories