-1

Say i have this function. In this function i create a Mollie payment using the Mollie Laravel API Wrapper. The function $molliePayment needs to be mocked as i don't want to call the API during testing. How can i achieve this?

 public static function create(string $price, string $description, string $redirectUrl, array $metadata)
    {
        $molliePayment = \Mollie\Laravel\Facades\Mollie::api()->payments->create([
            'amount' => [
                'currency' => 'EUR',
                'value' => number_format((float) $price, 2, '.', ''),
            ],
        ]);

        $payment->update(['mollie_id' => $molliePayment->id]);

        // redirect customer to Mollie checkout page
        redirect($molliePayment->getCheckoutUrl(), 303);

        return ['status' => 200];
    }

How can i mock this API call?

\Mollie\Laravel\Facades\Mollie::api()->payments->create([])

This is my test:

test('Mollie creates new payment', function ($order, $mollie) {

    // Mock Mollie API here

    // Call binded Mollie class
    (new App\Services\MollieService())->create(
        '10',
        'Test payment',
        'https://google.nl',
        ['team' => $order['team']]
    );

    $this->assertDatabaseHas('payments', [
        'price' => $mollie['amount']['value'],
    ]);

})->with('order', 'mollie');

Edit:

I've tried Mocking the facade using:

// Mock Mollie API here
   \Mollie\Laravel\Facades\Mollie::shouldReceive('api')->once()->andReturnSelf();

But when i do, i get the error:

Undefined property: Mockery_2_Mollie_Laravel_MollieManager::$payments
jadsghx
  • 3
  • 3
  • Since this is called a "façade" if it's a Laravel compatible façade then you can do what is described [here](https://laravel.com/docs/9.x/mocking#mocking-facades) – apokryfos Sep 19 '22 at 19:53
  • @apokryfos thanks for the reply. I've tried that but no luck. See my edit. – jadsghx Sep 20 '22 at 07:09
  • `andReturnSelf` means you are returning the same instance. I am assuming you actually want to return a object that matches the contract that is implemented by whatever `\Mollie\Laravel\Facades\Mollie::api()` returns – apokryfos Sep 20 '22 at 07:22
  • @apokryfos api() returns a new instance of ```MollieManager.php``` class. Do i need to go deeper? Do i also need to mock MollieManager and the dependencies inside the constructor of MollieManager? – jadsghx Sep 20 '22 at 07:47
  • You would need to mock `MollieManager` but you should not need to mock its constructor dependencies. (*)However given how you have written your code you may also need to mock whatever `MollieManager::payments` is which may or may not be one of the dependencies. – apokryfos Sep 20 '22 at 08:19
  • I'm kinda lost. Trying to wrap my head around this.. How would you do this? @apokryfos – jadsghx Sep 20 '22 at 15:03

1 Answers1

0

If you look through the code, you can actually see the api class, does not have properties but actually calls methods on the instance. This makes it possible to rewrite your logic into this.

Mollie::api()->payments()->create([...]);

This removes the property access, which makes it way more convenient to actually test. Mockery has a way to mock method chaining.

Mollie::shouldReceive('api->payments->create')->andReturn($payment);

To define the Payment, we have too look for what the api would return. This is also why mocking can be so hard, you have to understand how the calls are executed on code you do not own, while also understanding what it returns in combination with how you utilize the returned data.

Then fill out the id and the checkout url, as you use these. Offcourse declare this before the shouldReceive() call. Links is a stdClass object, therefor the casting of the array.

use Mollie\Api\Resources\Payment;

$payment = new Payment();
$payment->id = 42;
$payment->links = (object)['checkout' => 'https://some.checkout.url'];

Now you should be able to mock this facade combining this, with what you already have.

mrhn
  • 17,961
  • 4
  • 27
  • 46
  • A bit late, but thanks! This did the trick :) ensuring ```payment``` was changed to ```payment()``` made it super easy. Thanks! I've marked your response as the correct answer. – jadsghx Oct 18 '22 at 14:30
  • Glad it helped it was also fairly complex to understand how the molly api worked and actually could change to using payment() – mrhn Oct 18 '22 at 14:42