How to mock a paypal transaction in laravel with phpunit? - laravel

While testing:
While checkout items from my website, need to mock confirmation... so we can then continue processing the order. Where the testing can be done..
How would i swap out good code for a mock? such as:
$gateway = Omnipay::create('paypal');
$response = $gateway->purchase($request['params'])->send();
if ($response->isSuccessful()) { ... etc ...
How is this possible?
While i have created tests, my knowledge in the area of mocking is basic

As far as it depends t mocking, you don't need to know exact response, you just need to know inputs and outputs data and you should replace your service (Paypal in this case) in laravel service provider. You need some steps like bellow:
First add a PaymentProvider to your laravel service provider:
class AppServiceProvider extends ServiceProvider
{
...
/**
* Register any application services.
*
* #return void
*/
public function register()
{
$this->app->bind(PaymentProviderInterface::class, function ($app) {
$httpClient = $this->app()->make(Guzzle::class);
return new PaypalPackageYourAreUsing($requiredDataForYourPackage, $httpClient);
});
}
...
}
Then in your test class you should replace your provider with a mock version of that interface:
class PaypalPackageTest extends TestCase
{
/** #test */
public function it_should_call_to_paypal_endpoint()
{
$requiredData = $this->faker->url;
$httpClient = $this->createMock(Guzzle::class);
$paypalClient = $this->getMockBuilder(PaymentProviderInterface::class)
->setConstructorArgs([$requiredData, $httpClient])
->setMethod(['call'])
->getMock();
$this->instance(PaymentProviderInterface::class, $paypalClient);
$paypalClient->expects($this->once())->method('call')->with($requiredData)
->willReturn($httpClient);
$this->assertInstanceOf($httpClient, $paypalClient->pay());
}
}

This is the approach I usually take when I have to mock methods that contain calls to external libraries (such as Omnipay in your case).
Your snippet isn't very extensive, but I'll assume your class looks something like this:
class PaymentProvider
{
public function pay($request)
{
$gateway = Omnipay::create('paypal');
$response = $gateway->purchase($request['params'])->send();
if ($response->isSuccessful()) {
// do more stuff
}
}
}
What I would do is refactor the class, so that the call to the external library is inside a separate method:
class PaymentProvider
{
protected function purchaseThroughOmnipay($params)
{
$gateway = Omnipay::create('paypal');
return $gateway->purchase($params)->send();
}
public function pay($request)
{
$response = $this->purchaseThroughOmnipay($request['params']);
if ($response->isSuccessful()) {
// do more stuff
}
}
}
Then, after this refactoring, in the test class we can take advantage of the many possibilities PHPunit's getMockBuilder gives us:
<?php
use PHPUnit\Framework\TestCase;
class PaymentProviderTest extends TestCase
{
protected $paymentProvider;
protected function setUp()
{
$this->paymentProvider = $this->getMockBuilder(\PaymentProvider::class)
->setMethods(['pay'])
->getMock();
}
public function testPay()
{
// here we set up all the conditions for our test
$omnipayResponse = $this->getMockBuilder(<fully qualified name of the Omnipay response class>::class)
->getMock();
$omnipayResponse->expects($this->once())
->method('isSuccessful')
->willReturn(true);
$this->paymentProvider->expects($this->once())
->method('purchaseThroughOmnipay')
->willReturn($omnipayResponse);
$request = [
// add relevant data here
];
// call to execute the method you want to actually test
$result = $this->paymentProvider->pay($request);
// do assertions here on $result
}
}
Some explanation of what's happening:
$this->paymentProvider = $this->getMockBuilder(\PaymentProvider::class)
->setMethods(['pay'])
->getMock();
This gives us a mock instance of the Payment class, for which pay is a "real" method whose actual code is actually executed, and all other methods (in our case, purchaseThroughOmnipay is the one we care about) are stubs for which we can override the return value.
In the same way, here we are mocking the response class, so that we can then control its behavoir and influence the flow of the pay method:
$omnipayResponse = $this->getMockBuilder(<fully qualified name of the Omnipay response class>::class)
->getMock();
$omnipayResponse->expects($this->once())
->method('isSuccessful')
->willReturn(true);
The difference here is that we are not calling setMethods, which means that all the methods of this class will be stubs for which we can override the return value (which is exactly what we are doing for isSuccessful).
Of course, in case more methods of this class are called in the pay method (presumably after the if), then you will probably have to use expect more than once.

Related

Mocking a service class inside controller

I am trying to write a Feature test for my controller. To simplify my current situation, imagine my controller looks like this:
public function store(Business $business)
{
try {
(new CreateApplicationAction())->execute($business);
} catch (Exception $e) {
return response()->json(['message' => 'error'], 500);
}
return response()->json(['message' => 'success']);
}
What I am trying to achieve is, instead of testing CreateApplication class logic inside my integration test, I want to write another unit test for it specifically.
Is there a way I can simply say CreateApplicationAction expects execute() and bypass testing inside it? (without executing the code inside execute())
/** #test */
public function can_create_application()
{
$business = Business:factory()->create();
$mock = $this->mock(CreateApplicationAction::class, function (MockInterface $mock) use ($business) {
$mock->shouldReceive('execute')
->once()
->with($business)
->andReturn(true);
});
$response = $this->post('/businesses/3/application', $data);
$response->assertOk();
}
I saw online that people create "MockCreateApplicationAction" class but if possible I don't want to create another class as I don't want any logic to be inside it at all.
Is it possible?
class CreateApplicationAction
{
public function execute($business) {
dd("A");
// Business Logic...
}
}
So when I do the Mock, dd() should never be called. Or I am going in the wrong direction?
You will need to use Laravels container to resolve your class. The basic approach is to use the resolve() method helper. PHP does not have dependency injection, so you need to use one to make it possible, in Laravel the container solves that.
resolve(CreateApplicationAction::class)->execute($business);
On constructors, controller methods, jobs, events, listeners and commands (rule of thumb if the method is named handle), you can inject classes into the parameters and they will resolve through the container.
public function store(Business $business, CreateApplicationAction $applicationAction)
{
try {
$applicationAction->execute($business);

what is the best way to move logic from controller to a service

my question is : How to take my business logic out of controller and transfer it to a service ???
I am currently using two separate controllers for Post model in Laravel. One for user-related logic and one for administrator-related logic.
pseudo code for AdminPostController :
class AdminPostController
{
public function index(MaybyIndexRequest $request)
{
$something = do lines of calculation
return return PostResource::collection($something);
}
public function storeAdminPost(StorePostRequest $request)
{
$something = do lines of calculation
return return PostStoreResource::collection($something);
}
}
pseudo code for UserPostController :
class UserPostController
{
public function maybyUserindex(AnotherIndexRequest $request)
{
$something = do lines of calculation
return return UserPostResource::collection($something);
}
public function storeUserPost(OtherStorePostRequest $request)
{
$something = do lines of calculation
return return UserPostStoreResource::collection($something);
}
}
I want to transfer the business logic of these two controllers to another class and call them with the help of, for example, a Facade like :
class AdminPostController
{
public function index(MaybyIndexRequest $request)
{
$something = PostService::($request);
return return PostResource::collection($something);
}
public function storeUserPost(StorePostRequest $request)
{
$something = PostService::Store($request);
return return PostStoreResource::collection($something);
}
}
But I do not know with what design patterns I should do this. Or what I'm looking for is not a good way to get the code out of the controller !!!
The way to solve this problem came to my mind :
factory pattern : a class that has two methods called user() and admin().
class PostFactory
{
public function AdminCommands()
{
return new AdminPostCommands(); // a class that contains admin
}
public function UserCommands()
{
return new UserPostCommands(); // a class that contains user related logics
}
}
That the user method returns an instance of UserPostCommands class (including the user's logic) and the AdminCommands class method (contains the's post logic) .... or :
class PostFactory
{
public function Factory(User $user)
{
if ($user->isAdmin){
return new AdminPostCommands(); // a class that contains admin
}else{
return new UserPostCommands(); // a class that contains user related logics
}
}
a class that it takes an instance of the authorized user to decide whether the AdminPostCommands OR UserPostCommands class should be Returned. each of these two classes(AdminPostCommands or UserPostCommands ) has different methods. For example, the user may not be able to delete a post . Of course, user-related methods will only be used in the user controller and vice versa.
Look's like you are deviating from the standard conventions of Laravel.
Perhaps, it might be worthwhile spending some time learning about them. For example, https://laravel.com/docs/8.x/controllers#resource-controllers on how to structure controllers.
storeUserPost should simply be store, you are in the UserPost controller, so it's implied that what you are storing will be a UserPost. Any logic required can be moved to an event or a service/utility class.
https://laravel.com/docs/8.x/events#introduction
A controller shouldn't, usually, be doing more than querying/storing/updating the model with the new data or calling other classes that perform logic and returning a view or redirect, generally speaking.

partially mocking a class without affecting the private properties in PHP

I have a class with a lot of methods in which I need to mock only one method due to some sql incompatibility between mysql and in-memory sqlite database.
class OrderService implements OrderServiceContract
{
protected $deliveryService;
public function __construct(Delivery $deliveryService) // DI injected object
{
...
$this->deliveryService = $deliveryService;
...
}
public function methodNeedstoBeMocked()
{
....some sql related code...
}
public function returnToWarehouse($orderId)
{
DB::transaction(function() use ($orderId) {
...
$this->deliveryService->someOtherMethod($orderId); // problematic external service call
...
});
}
}
Now in my test I partially mock this class according to this doc link, and I call the returnToWarehouse from test but then it says that
Error : Call to a member function returnToWarehouse() on null.
meaning that the property $deliveryService doesn't exist on mock.
My test Implementation is as follows.
/**
* #test
*/
public function an_order_can_be_returned_to_warehouse()
{
...
...
$this->partialMock(OrderService::class, function ($mock) {
$mock->shouldReceive('methodNeedstoBeMocked')->andReturn(collect([]));
});
$orderService = app(OrderService::class);
$orderService->markOrderReturnedToWarehouse($order->id); // here is the problem gets triggered.
...
//assertions
}
What might be going wrong here? and what are some ways to mitigate this? Appreciate your help in advance.
The issue here is that partial test doubles from Mockery do not call the original constructor. For more information, please read the documentation here.
Alternatively, you could consider mocking the "problematic" method a bit differently. For example, you could extract that logic to a repository (since you mention that it is dealing with the database layer) that can then be mocked during your test.
Usually, When I have to mock some third party services I set up a Mock Like this.
This way you can set up easily your DI services
<?php
if (app()->environment('testing')) {
$this->app->bind(Delivery::class, static function () {
$service = \Mockery::mock(Delivery::class);
$service->shouldReceive('someOtherMethod')->andReturn([]);
return $service;
});
}

Facades vs. Classes with static methods in Laravel

I was looking around the Laravel framework and some of their products and I noticed that Cashier is using the Casheir class with static methods compared to Socialite, which is used as a facade.
What are the benefits/downsides of building it one or the other way, or is there none at all?
I would like to build something myself, but I don't want to start building it as a class with static methods if building it as a facade is a better solution.
When you may need multiple implementations, a single interface can be defined through facade to simplify the code
Building it as a class with static methods:
When you have multiple classes you have to do something like this:
CashierOne::method, CashierTwo::method ....
Used as a facade:
According to what you bind to the container to switch the implementation
You only need to call through an interface:
// Define a Cashier Facade
class Cashier extends Facade
{
/**
* Get the registered name of the component.
*
* #return string
*/
protected static function getFacadeAccessor()
{
return 'cashier';
}
}
// In CashServiceProvider
$this->app->singleton('cashier', function ($app) {
return new CashierManager ($app);
});
// In CashierManager
public function gateway($name = null)
{
// get cashier implementation by name
}
public function __call($method, $parameters)
{
return $this->gateway()->$method(...$parameters);
}
// In Controller
Cashier::method
In addition, the facade is easier to test, check:
https://laravel.com/docs/5.8/facades#how-facades-work

ZF2 and EntityManager (Doctrine)

I have a problem. I try to get the Entity-Manager without a Controller, but I found no way.
At this time, I get the Entity-Manager like this:
(Controller)
public function getEntityManager()
{
if (null === $this->_em) {
$this->_em = $this->getServiceLocator()->get('Doctrine\ORM\EntityManager');
}
return $this->_em;
}
(Plugin)
public function getEntityManager()
{
if($this->_em == null){
$this->_em = $this->getController()->getServiceLocator()->get('doctrine.entitymanager.orm_default');
}
return $this->_em;
}
You see, I need allways a controller. But, if I need the EntityManager in a model, i have a problem. I can give the model the controller, but I think this is really a bad way.
Have you any idea to get the EntityManager without a controller?
The way I handle Doctrine is through Services, i do it like the following:
//some Controller
public function someAction()
{
$service = $this->getServiceLocator()->get('my_entity_service');
return new ViewModel(array(
'entities' => $service->findAll()
));
}
The Service->findAll() would look something like this:
public function findAll()
{
return $this->getEntityRepository()->findAll();
}
Now we need to define the my_entity_service. I do this inside my Module.php
public function getServiceConfig()
{
return array(
'factories' => array(
'my_entity_service' => 'Namespace\Factory\MyServiceFactory'
)
);
}
Next I create the Factory (note: this could also be done via anonymous function inside the Module.php)
<?php
namespace Namespace\Factory;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\ServiceManager\FactoryInterface;
use Namespace\Model\MyModel;
class MyServiceFactory implements FactoryInterface
{
/**
* Create service
*
* #param ServiceLocatorInterface $serviceLocator
* #return mixed
*/
public function createService(ServiceLocatorInterface $serviceLocator)
{
$myModel= new MyModel();
$myModel->setEntityManager($serviceLocator->get('Doctrine\ORM\EntityManager'));
return $myModel;
}
}
Now this is a lot to chew :D I totally get that. What is happening here is actually very simple though. Instead of creating your model and somehow get to the EntityManager, you call ZF2's ServiceManager to create the Model for you and inject the EntityManager into it.
If this is still confusing to you, I'll gladly try to explain myself better. For further clarification however I'd like to know about your use case. I.e.: what for do you need the EntityManager or where exactly do u need it.
This code example is outside of the question scope
Just to give you a live example of something I do via ServiceFactories with forms:
public function createService(ServiceLocatorInterface $serviceLocator)
{
$form = new ReferenzwertForm();
$form->setHydrator(new DoctrineEntity($serviceLocator->get('Doctrine\ORM\EntityManager')))
->setObject(new Referenzwert())
->setInputFilter(new ReferenzwertFilter())
->setAttribute('method', 'post');
return $form;
}
Your real question is "How to get an Instance of ServiceManager in my own classes"
For this, take a look at the docu: (bottom of page http://zf2.readthedocs.org/en/latest/modules/zend.service-manager.quick-start.html)
By default, the Zend Framework MVC registers an initializer that will
inject the ServiceManager instance, which is an implementation of
Zend\ServiceManager\ServiceLocatorInterface, into any class
implementing Zend\ServiceManager\ServiceLocatorAwareInterface. A
simple implementation looks like the following.
so implent the ServiceLocatorInterface in your classes and then inside your class you can call:
$this->getServiceLocator()->get('doctrine.entitymanager.orm_default');
or any other service you have registered.

Resources