Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DX] Static resource access is bad for mockability #41

Closed
kenloburn opened this issue Aug 22, 2017 · 5 comments
Closed

[DX] Static resource access is bad for mockability #41

kenloburn opened this issue Aug 22, 2017 · 5 comments

Comments

@kenloburn
Copy link

In the current implementation of the SDK static methods on classes are used to make requests.

For example:

// ...
$users = User::listing($apiContext)->getValue();

This is not good for developer experience as it makes it non-trivial to implement the SDK in a project with high unit test coverage.

Imagine for example that in my project I have a FinanceService that has a method to accumulate total spend on saturday + sunday. If I expressed the service like this:

<?php

namespace Bla;

use bunq\Context\ApiContext;
use bunq\Model\Generated\Payment;

class FinanceService
{
    private $apiContext;

    public function __construct(ApiContext $apiContext)
    {
        $this->apiContext = $apiContext;
    }

    public function getWeekendSpending($userId, $monetaryAccountId)
    {
        $amount = 0;
        $paymentList = Payment::listing($this->apiContext, $userId, $monetaryAccountId);

        foreach ($paymentList as $payment) {
            $date = new \DateTime($payment->getCreated());

            if (in_array($date->format('D'), ['Sat', 'Sun'])) {
                $amount += $payment->getAmount();
            }
        }

        return $amount;
    }
}

Writing the unit test would not allow me to mock the API HTTP request very easily as there is no simple way to ensure that Payment::listing does not make an actual HTTP request, or to influence what the return value would be.

There is a workaround I could do on my end, but it involves wrapping each individual resource that makes HTTP requests in a wrapper object like this:

class PaymentResource implements PaymentResourceInterface
{
    private $apiContext;
    
    public function __construct(ApiContext $apiContext)
    {
        $this->apiContext = $apiContext;
    }

    public function listing($userId, $monetaryAccountId)
    {
        return Payment::listing($this->apiContext, $userId, $monetaryAccountId);
    }
}

To be able to mock (the result of) API HTTP requests in a unit test designed to test only the logic of the service method, and not invoke an actual API HTTP request (or even obtain/instantiate an ApiContext/session with the API):

class FinanceServiceTest extends \PHPUnit\Framework\TestCase
{
    public function testGetWeekendSpending()
    {
        // Friday
        $payment0 = new Payment();
        $payment0->setAmount(new Amount('199', 'EUR'));
        $payment0->setCreated('2017-08-18 10:33:17.097996');

        // Sunday
        $payment1 = new Payment();
        $payment1->setAmount(new Amount('199', 'EUR'));
        $payment1->setCreated('2017-08-20 16:21:57.295726');

        $paymentResource = $this
            ->createMock(PaymentResource::class)
            ->method('listing')
            ->willReturn([
                $payment0,
                $payment1
            ]);


        $s = new FinanceService($paymentResource);
        $this->assertEquals(199, $s->getWeekendSpending(1, 2));
    }
}

This would mean that I would end up maintaining code that is domain specific to bunq but somehow physically located in the codebase of my own projects.

Ideally, the SDK is designed in a way that there is no need to invoke static methods to make API HTTP requests - this will make the DX much smoother in modern PHP projects.

@dnl-blkv
Copy link
Contributor

@kenloburn great point!

@kenloburn
Copy link
Author

@OGKevin @dnl-blkv Any idea if/when this will be looked into?

@dnl-blkv
Copy link
Contributor

@kenloburn this is a major rework. So, not providing any dates yet.

@OGKevin
Copy link
Contributor

OGKevin commented Nov 10, 2017

@kenloburn Just a little update, we haven't forgot about this. It will be on the roadmap after callback models, decoder improvement for callback models and some minor patches 👍

@OGKevin
Copy link
Contributor

OGKevin commented Apr 7, 2018

After internal discussions, we concluded that ws will not move away from the static HTTP methods.

I would suggest you create a mock object and mock the static call via this mock object.

@OGKevin OGKevin closed this as completed Apr 7, 2018
1.0.0 - SDK automation moved this from To do to Done Apr 7, 2018
@OGKevin OGKevin removed this from Done in 1.0.0 - SDK Apr 7, 2018
@OGKevin OGKevin removed this from the 0.13.5 milestone Apr 7, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants