mocking mongodb with phpunit - php

I have a save function for create new document with mongoDB
public function save(User $user): User
{
$result = $this->usersCollection->insertOne($user->getUser());
$user->setId($result->getInsertedId());
return $user;
}
And Change __construct for implemet test
public function __construct($db = null)
{
if (is_null($db)) {
parent::__construct();
} else {
$this->db = $db;
}
$this->usersCollection = $this->db->users;
}
I write this test for save function
public function testSave()
{
$mongo = \Mockery::mock('MongoDB\Client');
$mongo->shouldReceive('insertOne')->andReturn("ok");
$mongo->shouldReceive('selectDatabase')->andReturnSelf();
$user = new User('jack', '0015005050');
$um = new UserMongoDB($mongo);
$res = $um->save($user);
}
everything works well but my problem is $result->getInsertedId() How to I can Mock this function?
Error : Call to a member function getInsertedId() on string

The return type of the insertOne method must be an instance of InsertOneResult (see docs). At the moment you are returning the string "ok". You could keep going and make insertOne return a mock of InsertOneResult. This may work but you are at the gate to mocking hell. Personally, I'd write integration tests for the save method. Mocking the save method in other unit tests is way easier than mocking the low level MongoDB stuff all over the place.

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.

PHPUnit Mock Function Call

I have the following function within an object that I wish to test
public function getUser($credentials, UserProviderInterface $userProvider)
{
$resourceOwner = $this->getAuthClient()->fetchUserFromToken($credentials);
// ... Code to create user if non-existent, update values, etc.
// ... Basically the code I want to test is here
return $user;
}
The getAuthClient() call returns a Client object with the available function of fetchUserFromToken
How can I, in a PHPUnit test, mock the fetchUserFromToken to just return a ResourceOwner object? Because the actual function does a lot of authentication mechanisms and is out of the scope of this test case
I found a php library called Runkit, but that is not the approach I want to peruse. It feels hacky and overkill for the problem at hand.
The getAuthClient() function is defined as follows
private function getAuthClient() {
return $this->clients->getClient('auth');
}
With $this->clients being defined by the constructor
public function __construct(ClientRepo $clients) {
$this->clients = $clients;
}
So, I mocked the ClientRepo and exposed the getClient() method to return a Mock of the AuthClient (regardless of input) so that I can control the return of fetchUserFromToken() call.
public function testGetUser() {
$client = $this->createMock(WebdevClient::class);
$client->expects($this->any())
->method('fetchUserFromToken')
->will($this->returnCallback(function()
{
// TARGET CODE
}));
$clients = $this->createMock(ClientRegistry::class);
$clients->expects($this->any())
->method('getClient')
->will($this->returnCallback(function() use ($client)
{
return $client;
}));
$object = new TargetObject($clients);
$result = $object->getUser(...);
// ... Assertions to follow
}

PHP Laravel are DB function results cached?

Noob question. I'm working on a project which involves interacting with some legacy software and the database is not compatible with regular Laravel Relationships.
If I'm defining things in a constructor like this:
public function __construct(array $attributes = array())
{
parent::__construct($attributes);
$this->vatpercentage = $this->customer()->vatpercentage;
$this->vatcode = $this->customer()->vatcode;
$this->partrevision = $this->part()->revision;
$this->unitprice = $this->part()->unitprice;
}
public function part(){
return Part::findOrFail($this->partnum);
}
public function customer(){
$traderid = Order::where('id', $this->orderid)->value('traderid');
return Customer::where('id', $traderid)->where('tradertype', 'C')->first();
}
I need to reference customer(), part() and other similar functions many times in the constructor. Is the DB getting queried every time I reference $this->customer(), etc or is the result cached the first time I reference it and then used for all the other times following? Basically am I doing a lot of unnecessary DB calls by coding in this way rather than setting $this->customer = $this->customer() and grabbing the values like $this->customer->example?
No database query or method call is going to be cached automatically in your application, nor should it. Laravel and PHP aren't going to know how you want to use queries or methods.
Everytime you call customer(), you're building up and executing a new query. You could easily cache the result in a property if that's what you want, but you'd have watch the value of the $orderid property:
protected $customerCache;
public function customer()
{
if ($customerCache) return $customerCache;
$traderid = Order::where('id', $this->orderid)->value('traderid');
return $customerCache = Customer::where('id', $traderid)->where('tradertype', 'C')->first();
}
You're also performing too much in your constructor. I would highly recommend not performing queries in any constructors, constructors should be used to pass dependencies. The way you have it designed would make it very hard to unit test.
In Laravel 4.* there was a remember() method handling the cache in queries. It was removed from 5.1 with a valid reason that it's not the responsibility of Eloquent neither the query Builder to handle cache. A very very simplified version of a decorator class that could handle the caching of your queries:
final class CacheableQueryDecorator
{
private $remember = 60; //time in minutes, switch to load from config or add a setter
private $query = null;
public function __construct(Builder $builder)
{
$this->query = $builder;
}
private function getCacheKey(string $prefix = ''):string
{
return md5($prefix . $this->query->toSql() . implode(',', $this->query->getBindings()));
}
public function __call($name, $arguments)
{
$cache = Cache::get($this->getCacheKey($name), null);
if ($cache) {
return $cache;
}
$res = call_user_func_array([$this->query, $name], $arguments);
Cache::put($this->getCacheKey($name), $res, $this->remember);
return $res;
}
}
Using it:
$results = (new CacheableQueryDecorator($query))->get()

ZF2 phpunit Zend Logger

I am trying to write unit test for my application. which as logging the information functionality.
To start with i have service called LogInfo, this how my class look like
use Zend\Log\Logger;
class LogInfo {
$logger = new Logger;
return $logger;
}
I have another class which will process data. which is below.
class Processor
{
public $log;
public function processData($file)
{
$this->log = $this->getLoggerObj('data');
$this->log->info("Received File");
}
public function getLoggerObj($logType)
{
return $this->getServiceLocator()->get('Processor\Service\LogInfo')->logger($logType);
}
}
here i am calling service Loginfo and using it and writing information in a file.
now i need to write phpunit for class Processor
below is my unit test cases
class ProcessorTest{
public function setUp() {
$mockLog = $this->getMockBuilder('FileProcessor\Service\LogInfo', array('logger'))->disableOriginalConstructor()->getMock();
$mockLogger = $this->getMockBuilder('Zend\Log\Logger', array('info'))->disableOriginalConstructor()->getMock();
$serviceManager = new ServiceManager();
$serviceManager->setService('FileProcessor\Service\LogInfo', $mockLog);
$serviceManager->setService('Zend\Log\Logger', $mockLogger);
$this->fileProcessor = new Processor();
$this->fileProcessor->setServiceLocator($serviceManager);
}
public function testProcess() {
$data = 'I have data here';
$this->fileProcessor->processData($data);
}
}
I try to run it, i am getting an error "......PHP Fatal error: Call to a member function info() on a non-object in"
i am not sure , how can i mock Zend logger and pass it to class.
Lets check out some of your code first, starting with the actual test class ProcessorTest. This class constructs a new ServiceManager(). This means you are going to have to do this in every test class, which is not efficient (DRY). I would suggest constructing the ServiceMananger like the Zend Framework 2 documentation describes in the headline Bootstrapping your tests. The following code is the method we are interested in.
public static function getServiceManager()
{
return static::$serviceManager;
}
Using this approach makes it possible to obtain the instance of ServiceManager through Bootstrap::getServiceManager(). Lets refactor the test class using this method.
class ProcessorTest
{
protected $serviceManager;
protected $fileProcessor;
public function setUp()
{
$this->serviceManager = Bootstrap::getServiceManager();
$this->serviceManager->setAllowOverride(true);
$fileProcessor = new Processor();
$fileProcessor->setServiceLocator($this->serviceManager);
$this->fileProcessor = $fileProcessor;
}
public function testProcess()
{
$mockLog = $this->getMockBuilder('FileProcessor\Service\LogInfo', array('logger'))
->disableOriginalConstructor()
->getMock();
$mockLogger = $this->getMockBuilder('Zend\Log\Logger', array('info'))
->disableOriginalConstructor()
->getMock();
$serviceManager->setService('FileProcessor\Service\LogInfo', $mockLog);
$serviceManager->setService('Zend\Log\Logger', $mockLogger);
$data = 'I have data here';
$this->fileProcessor->processData($data);
}
}
This method also makes it possible to change expectations on the mock objects per test function. The Processor instance is constructed in ProcessorTest::setUp() which should be possible in this case.
Any way this does not solve your problem yet. I can see Processor::getLoggerObj() asks the ServiceManager for the service 'Processor\Service\LogInfo' but your test class does not set this instance anywhere. Make sure you set this service in your test class like the following example.
$this->serviceManager->setService('Processor\Service\LogInfo', $processor);

PropelORM, Symfony 2 and Unit testing

I'm used to the habit of writing like this:
$results = SomeModelQuery::create()->filterByFoo('bar')->find();
However this does not scale for unit testing because I can't inject a mock object, i.e. I can't affect what data is returned. I'd like to use fixture data, but I can't.
Nor does it seem great to inject an object:
class Foo
{
public __construct($someModelQuery)
{
$this->someModelQuery = $someMOdelQuery;
}
public function doSthing()
{
$results = $this->someModelQuery->filterByFoo('bar')->find();
}
}
DI feels horrible. I have tens of query objects to mock and throw. Setting through constructor is ugly and painful. Setting using method is wrong because it can be forgotten when calling. And it feels painful to always for every single lib and action to create these query objects manually.
How would I elegantly do DI with PropelORM query classes? I don't want to call a method like:
$oneQuery = OneQuery::create();
$anotherQuery = AnotherQuery::create();
// ... 10 more ...
$foo = new Foo($oneQuery, $anotherQuery, ...);
$foo->callSomeFunctionThatNeedsThose();
In my opinion (and Martin Folowers's) there is a step between calling everything statically and using Dependency Injection and it may be what you are looking for.
Where I can't do full DI (Zend Framework MVC for example) I will use a Service Locator. A Service Layer will be the place that all your classes go to get there dependencies from. Think of it as a one layer deep abstraction for your classes dependencies. There are many benefits to using a Service Locator but I will focus on testability in this case.
Let's get into some code, here is are model query class
class SomeModelQuery
{
public function __call($method, $params)
{
if ($method == 'find') {
return 'Real Data';
}
return $this;
}
}
All it does is return itself unless the method 'find' is called. Then is will return the hard-coded string "Real Data".
Now our service locator:
class ServiceLocator
{
protected static $instance;
protected $someModelQuery;
public static function resetInstance()
{
static::$instance = null;
}
public static function instance()
{
if (self::$instance === null) {
static::$instance = new static();
}
return static::$instance;
}
public function getSomeModelQuery()
{
if ($this->someModelQuery === null) {
$this->someModelQuery = new SomeModelQuery();
}
return $this->someModelQuery;
}
public function setSomeModelQuery($someModelQuery)
{
$this->someModelQuery = $someModelQuery;
}
}
This does two things. Provides a global scope method instance so you can always get at it. Along with allowing it to be reset. Then providing get and set methods for the model query object. With lazy loading if it has not already been set.
Now the code that does the real work:
class Foo
{
public function doSomething()
{
return ServiceLocator::instance()
->getSomeModelQuery()->filterByFoo('bar')->find();
}
}
Foo calls the service locator, it then gets an instance of the query object from it and does the call it needs to on that query object.
So now we need to write some unit tests for all of this. Here it is:
class FooTest extends PHPUnit_Framework_TestCase
{
protected function setUp()
{
ServiceLocator::resetInstance();
}
public function testNoMocking()
{
$foo = new Foo();
$this->assertEquals('Real Data', $foo->doSomething());
}
public function testWithMock()
{
// Create our mock with a random value
$rand = mt_rand();
$mock = $this->getMock('SomeModelQuery');
$mock->expects($this->any())
->method('__call')
->will($this->onConsecutiveCalls($mock, $rand));
// Place the mock in the service locator
ServiceLocator::instance()->setSomeModelQuery($mock);
// Do we get our random value back?
$foo = new Foo();
$this->assertEquals($rand, $foo->doSomething());
}
}
I've given an example where the real query code is called and where the query code is mocked.
So this gives you the ability to inject mocks with out needing to inject every dependency into the classes you want to unit test.
There are many ways to write the above code. Use it as a proof of concept and adapt it to your need.

Categories