Passing value object to a Job makes the unit tests fail - php

I'm using the latest of the Lucid architecture approach. Now I want to test a Feature with a few Jobs and Operations.
I'm also using value objects. I've rolled my own but followed some best practices. It shouldn't matter since Jobs can accept any class in both the constructor and the handle() method.
If I pass a value object to the constructor, testing the Job calls from the Feature are failing.
I mock the Job with use of the mock() static method that comes with the provided Job unit from which I extend the other jobs. I pass the arguments correct, otherwise it would fail. So that's working.
But the test fails when I pass a value object and the UnitDispatcher (part of Lucid) detects it's about a unit test.
Here's the Job:
class RetrieveMessageJob extends Job
{
private UniqueId $uniqueId;
public function __construct(UniqueId $uniqueId)
{
$this->uniqueId = $uniqueId;
}
public function handle(ServiceProvider $provider)
{
return $provider->retrieveMessage($this->uniqueId);
}
}
This is the value object:
class UniqueId
{
public string $value;
public function __construct($value)
{
$this->value = $value;
}
public function __toString()
{
return (string) $this->value;
}
public function value()
{
return (string) $this->value;
}
}
Since UnitDispatcher is using reflection to resolve the properties to compare, it tries to find the value of the property in a different way. None the less, it tries to compare some values in the
getConstructorExpectationsForInstance() method:
public function getConstructorExpectationsForInstance($unit)
{
foreach ($this->constructorExpectations as $index => $args) {
$expected = new $unit(...$args);
$ref = new ReflectionClass($unit);
// we start by assuming that the unit instance and the $expected one are equal
// until proven otherwise when we find differences between properties.
$isEqual = true;
foreach ($ref->getProperties() as $property) {
if ($property->getValue($unit) !== $property->getValue($expected)) {
$isEqual = false;
break;
}
}
if ($isEqual) {
return $this->constructorExpectations[$index];
}
}
}
Both $property->getValue($unit) and $property->getValue($expected) return a UniqueId object with exact the same value as expected by me. Changing the visibility of the property has no effect.
Technically the !== should not be true because of the same value. So I have no idea what else the comparison tries to find.
I'm a bit lost on why this keeps failing.
Here's the test I'm trying to pass:
class SyncMessagesFeatureTest extends TestCase
{
protected function setUp(): void
{
$this->feature = new SyncMessagesFeature();
}
public function testHandle()
{
$result = 'Irrelevant for this problem';
RetrieveMessageJob::mock(['uniqueId' => new UniqueId('uniqueId-1')])->shouldReturn($result);
$this->feature->handle();
}
}
Thanks for your time to read this.

Related

How to avoid internal calling function when running PHPUnit test? And how to set mock data for internal performance?

I have a class Receipt.php
<?php
namespace TDD;
class Receipt {
private $user_id = 1;
private $pending_amount;
public function total(array $items = []){
$items[] = $this->pending();
return array_sum($items);
}
public function tax($amount,$tax){
return $amount * $tax;
}
private function pending()
{
$sql = 'select pending_amount from Pending_transtions where user_id =' . $this->user_id . ' limit 1;';
//$pending_amt = $this->mainDb->get_sql_row($sql);
//$this->pending = $pending_amt['pending_amount'];
return $this->pending_amount = 45;
}
public function addTaxPending($tax){
return $this->pending_amount * $tax;
}
}?>
And in my PHPUnit file, ReceiptTest.php
<?php
namespace TDD\Test;
require(dirname(dirname(__FILE__))).DIRECTORY_SEPARATOR.'vendor'.DIRECTORY_SEPARATOR.'autoload.php';
use PHPUnit\Framework\TestCase;
use TDD\Receipt;
class ReceiptTest extends TestCase{
public function setUp(): void {
$this->Receipt = new Receipt();
}
public function tearDown(): void{
unset($this->Receipt);
}
public function testTotal(){
$input = [0,2,5,8];
$output = $this->Receipt->total($input);
$this->assertEquals(15,$output,"this is not valid");
}
public function testTax(){
$inputAmount = 10.00;
$inputTax = 0.10;
$output = $this->Receipt->tax($inputAmount,$inputTax);
$this->assertEquals(1.0,$output,"this tax expecting 1.0");
}
}?>
question:
How to ignore internal calling function pending() because it fetches data from DB. At the same time I want to access the property of $this->pending_amount.
Here Pending() must be private function.
How can I achieve that? I am looking for your valuable solutions
Proper solution is to replace your dependency (the one which is saved under $this->mainDb in your example) with a "mocked" one in your test case.
Here is an article from PHPUnit manual, which shows how to create mocks - https://phpunit.readthedocs.io/en/9.5/test-doubles.html#mock-objects
--
Speaking about ways of injection: your can either pass $this->mainDb instance via class constructor, or make so-called "seam" in form of public setMainDb method (which is not an elegant solution - I'd prefer to avoid it).
Another thing which I had to do sometimes, is to replace the value via Reflection: make private property accessible and set it inside of test to the value I need.
--
Update:
Based on given example, I think the easiest way to achieve desired result is:
Change test case's setUp to:
public function setUp(): void
{
$this->Receipt = new Receipt();
$mainDbMock = new class() {
public function get_sql_row() {
return [
"pending_amount" => 0
];
}
};
$this->Receipt->setMainDb($mainDbMock);
}
Add "seam"-method to your Receipt class:
public function setMainDb($mainDb)
{
$this->mainDb = $mainDb;
}
You need to mock the dependency. The dependency in your case is the database. You need to replace access to the database with a mock that you can configure with known return values.
Take the time to read the PHPUnit documentation on how to use mock objects.
A basic example
Given a DB access class with a get_sql_row method that accepts an SQL string and returns some data structure, we don't need to fill in any code here in our example because we're going to mock the get_sql_row method and configure it to return known values for our test.
class MainDb
{
public function get_sql_row(string $sql)
{
// ...
}
}
Our receipt class accepts a DB access object in the constructor and uses it to execute SQL e.g. the pending method executes SQL to get the pending amount (if any).
class Receipt
{
public function __construct($mainDb)
{
$this->mainDb = $mainDb;
}
public function total(array $items = [])
{
$items[] = $this->pending();
return array_sum($items);
}
private function pending()
{
$pendingAmount = $this->mainDb->get_sql_row('sql...');
return $pendingAmount['pending_amount'];
}
}
In the testcase setup method we create a mock of the DB access class and create the receipt object with the mocked database. The mock is then used in the tests to configure expectations, received arguments, and return values.
The testTotalWithNoPendingAmount test configures the DB mock to return 0 when get_sql_row is invoked. The get_sql_row is only expected to be called once and will fail otherwise. Configuring get_sql_row to return a known value allows us to test the behaviour of the receipt class without touching the DB.
Similarly, testTotalWithPendingAmount configures the DB mock get_sql_row to return a value other than 0 so that we can test behaviour of calculating the total when there is a pending amount.
final class ReceiptTest extends TestCase
{
protected Receipt $receipt;
protected function setUp(): void
{
// create a mock of the database access object
$this->dbMock = $this->createMock(MainDb::class);
// create receipt with mocked database
$this->receipt = new Receipt($this->dbMock);
}
public function testTotalWithNoPendingAmount(): void
{
$this->dbMock->expects($this->once())
->method('get_sql_row')
->willReturn(['pending_amount' => 0]);
$this->assertEquals(15, $this->receipt->total([0, 2, 5, 8]));
}
public function testTotalWithPendingAmount(): void
{
$this->dbMock->expects($this->once())
->method('get_sql_row')
->willReturn(['pending_amount' => 3]);
$this->assertEquals(18, $this->receipt->total([0, 2, 5, 8]));
}
}
Take the time to read the PHPUnit documentation on using mock objects and understand how they work and how to use them in your testing.
Note also that your handling of SQL is potentially open to SQL injection. Please read about SQL injection.

PHP unit testing External static method call from different class

I am trying to write a unit test for a function that immediately loads an object from a different class that uses the input to the function as a parameter. I am new to php unit testing and couldn't find anything that address my particular problem. A few leads that I had that led to no avail was using an injector, and trying to us a reflection.
The code I am trying to write a unit test for is:
public static function isUseful($item) {
$objPromo = MyPromoCodes::Load($item->SavedSku);
if (!is_null($objPromo)
&& ($objPromo->PromoType == MyPromoCodes::Interesting_Promo_Type)) {
return true;
}
return false;
}
My attempt at mocking this out:
public function testIsUseful() {
$injector = $this->getMockBuilder('MyPromoCodes')
->setMethods(array('Load'))
->getMock();
$objPromo = $this->getMock('MyPromoCodes');
$objPromo->PromoType = 'very interesting promo type';
$injector->set($objPromo, 'MyPromoCodes');
$lineItem1 = $this->getDBMock('LineItem');
$this->assertTrue(MyClass::isUseful($lineItem1));
}
however this doesn't work because there is no set method for this object....
Not sure what else to try, any help would be appreciated.
I made the library that makes static classes mocking possible:
class MyClass {
public static $myPromoCodes = 'myPromoCodes';
public static function isUseful($item) {
$objPromo = self::$MyPromoCodes::Load($item->SavedSku);
if (!is_null($objPromo)
&& ($objPromo->PromoType == MyPromoCodes::Interesting_Promo_Type)) {
return true;
}
return false;
}
}
class MyClassTest extends \PHPUnit_Framework_TestCase
{
public function testSomething()
{
$myClass = Moka::stubClass('MyClass');
$myClass::$myPromoCodes = Moka::stubClass(null, ['::Load' => (object)[
'PromoType' => MyPromoCodes::Interesting_Promo_Type
]]);
$this->assertTrue($myClass::isUseful((object)['SavedSku' => 'SKU']);
$this->assertEquals([['SKU']], $myClass::$myPromoCodes->moka->report('::Load'));
}
}
To start with you cannot mock static method with PHPUnit. At least not with 4.x and 5.x.
I would suggest a DI approach like this:
class MyClass
{
private $promoCodesRepository;
public function __construct(MyPromoCodesRepository $promoCodesRepository)
{
$this->promoCodesRepository = $promoCodesRepository;
}
public function isUseful(MyItem $item)
{
$objPromo = $this->promoCodesRepository->Load($item->SavedSku);
// ...
}
}
Here you can easily mock the Load method.
Unfortunately the "static" approach creates a lot of issues during tests so it is better to avoid it whenever possible.

Advantage of PHP SoapClient classmap

Can anyone tell me the advantage of using the classmap option within PHP Soapclient? Maybe with some practical examples?
The classmap option can be used to map some WSDL types to PHP classes.
Example,
class MyLoginResult {
protected $serverUrl;
protected $sessionId;
public function getServerUrl()
{
return $this->serverUrl;
}
public function getSessionId()
{
return $this->sessionId;
}
public function getServerInstance()
{
$match = preg_match(
'/https:\/\/(?<instance>[^-]+)\.example\.com/',
$this->serverUrl,
$matches
);
return $matches['instance'];
}
}
$client = new SoapClient("books.wsdl",
array('classmap' => array('LoginResult' => "MyLoginResult")));
$loginResult = $client->getLoginResult();
$instance = $loginResult->getServerInstance();
As addition to the comment by hoangthienan, I would show one more advantage when using a mapped class.
E.g. you could extend the class by a __set() method, that would be triggered when the SoapClient passes its data to the mapped class (you should know, the method will not be triggered if your property is public).
In that case you can alternate the data passed from SoapClient before you assign it to your Data-Class.
class MyLoginResult {
protected $serverUrl;
protected $sessionId;
private $is_logged_in;
public function __set($name, $value) {
if ($name == 'login_status') {
$this->is_logged_in = ($value == 'logged_in') ? true : false;
} else {
$this->$name = $value;
}
}
public function loginSuccessfull() {
return $this->is_logged_in;
}
// class code from hoangthienan
}
e.g. in this example we get a string from Soap, but we store a bool-value in our class.
You could use this for other changes to e.g. if you like to store your internal variables in a array instead of using direct properties.

PHPUnit Test How Many Times A Function Is Called

I'm working on a test in phpunit and I'm running into an issue. I have a public function on my class that I am trying to test. Depending on the parameters passed in to the method, a protected function also in my test class will be called one or two times. I currently have a test in place to check that the return data is correct, but I would also like to make sure the protected method is being called the correct number of times.
I know that a mock object will allow me to count the number of times a function is called, but it will also override the value returned by the protected function. I tried using a mock object with no "will" section, but it would just return null, not the actual value for the protected method.
ExampleClass
public function do_stuff($runTwice){
$results = do_cool_stuff();
if($runTwice){
$results = 2 * do_cool_stuff();
}
return $results;
}
protected function do_cool_stuff()
{
return 2;
}
In my test, I want to check whether do_cool_stuff() was called once or twice, but I still want the return values of both functions to be the same so I can test those as well in my unit test.
tl;dr
I want to count the number of times a protected method in my test object is called (like you can do with a mock object) but I still want all the methods in my test method to return their normal values (not like a mock object).
Alternatively, revert back to rolling your own testable stand-in. The following aint pretty, but you get the idea:
class ExampleClass {
public function do_stuff($runTwice) {
$results = $this->do_cool_stuff();
if ($runTwice) {
$results = 2 * $this->do_cool_stuff();
}
return $results;
}
protected function do_cool_stuff() {
return 2;
}
}
class TestableExampleClass extends ExampleClass {
/** Stores how many times the do_cool_stuff method is called */
protected $callCount;
function __construct() {
$this->callCount = 0;
}
function getCallCount() {
return $this->callCount;
}
/** Increment the call counter, and then use the base class's functionality */
protected function do_cool_stuff() {
$this->callCount++;
return parent::do_cool_stuff();
}
}
class ExampleClassTest extends PHPUnit_Framework_TestCase {
public function test_do_stuff() {
$example = new ExampleClass();
$this->assertEquals(2, $example->do_stuff(false));
$this->assertEquals(4, $example->do_stuff(true));
}
public function test_do_cool_stuff_is_called_correctly() {
// Try it out the first way
$firstExample = new TestableExampleClass();
$this->assertEquals(0, $firstExample->getCallCount());
$firstExample->do_stuff(false);
$this->assertEquals(1, $firstExample->getCallCount());
// Now test the other code path
$secondExample = new TestableExampleClass();
$this->assertEquals(0, $secondExample->getCallCount());
$secondExample->do_stuff(true);
$this->assertEquals(2, $secondExample->getCallCount());
}
}
I wonder though whether counting the number of times a protected method has been called is really a good test. It's coupling your test to the implementation pretty hard. Does it really matter whether it is called twice, or are you more interested in the interactions with other objects? Or maybe this is pointing towards do_cool_stuff needing a refactor into two separate methods:
class ExampleClass {
public function do_stuff($runTwice) {
if ($runTwice) {
return $this->do_cool_stuff_twice();
} else {
return $this->do_cool_stuff_once();
}
}
//...
}
Try setting a global variable prior to utilizing the class.
$IAmDeclaredOutsideOfTheFunction;
then use it to store the count and simply check it after your functions and classes have been called.

unit testing datastores in PHP

I'm using PHPUnit but find it difficult to make it create good mocks and stubs for objects used as datastore.
Example:
class urlDisplayer {
private $storage;
public function __construct(IUrlStorage $storage) { $this->storage = $storage; }
public function displayUrl($name) {}
public function displayLatestUrls($count) {}
}
interface IUrlStorage {
public function addUrl($name, $url);
public function getUrl($name);
}
class MysqlUrlStorage implements IUrlStorage {
// saves and retrieves from database
}
class NonPersistentStorage implements IUrlStorage {
// just stores for this request
}
Eg how to have PHPUnit stubs returning more than one possible value on two calls with different $names?
Edit: example test:
public function testUrlDisplayerDisplaysLatestUrls {
// get mock storage and have it return latest x urls so I can test whether
// UrlDisplayer really shows the latest x
}
In this test the mock should return a number of urls, however in the documentation I only how to return one value.
Your question is not very clear - but I assume you are asking how to use phpunit's mock objects to return a different value in different situations?
PHPUnit's mock classes allow you specify a custom function (ie: a callback function/method) - which is practically unlimited in what it can do.
In the below example, I created a mock IUrlStorage class that will return the next url in its storage each time it is called.
public function setUp()
{
parent::setUp();
$this->fixture = new UrlDisplayer(); //change this to however you create your object
//Create a list of expected URLs for testing across all test cases
$this->expectedUrls = array(
'key1' => 'http://www.example.com/url1/'
, 'key2' => 'http://www.example.net/url2/'
, 'key3' => 'http://www.example.com/url3/'
);
}
public function testUrlDisplayerDisplaysLatestUrls {
//Init
$mockStorage = $this->getMock('IUrlStorage');
$mockStorage->expects($this->any())
->method('getUrl')
->will( $this->returnCallback(array($this, 'mockgetUrl')) );
reset($this->expectedUrls); //reset array before testing
//Actual Tests
$this->assertGreaterThan(0, count($this->expectedUrls));
foreach ( $this->expectedUrls as $key => $expected ) {
$actual = $this->fixture->displayUrl($key);
$this->assertEquals($expected, $actual);
}
}
public function mockGetUrl($name)
{
$value = current($this->expectedUrls);
next($this->expectedUrls);
//Return null instead of false when end of array is reached
return ($value === false) ? null : $value;
}
Alternatively, sometimes it is easier to simply create a real class that mocks up the necessary functionality. This is especially easy with well defined and small interfaces.
In this specific case, I would suggest using the below class instead:
class MockStorage implements IUrlStorage
{
protected $urls = array();
public function addUrl($name, $url)
{
$this->urls[$name] = $url;
}
public function getUrl($name)
{
if ( isset($this->urls[$name]) ) {
return $this->urls[$name];
}
return null;
}
}
?>
Then in your unit test class you simply instantiate your fixture like below:
public function setUp() {
$mockStorage = new MockStorage();
//Add as many expected URLs you want to test for
$mockStorage->addUrl('name1', 'http://example.com');
//etc...
$this->fixture = new UrlDisplayer($mockStorage);
}

Categories