如何使用phpunit模拟laravel中的paypal事务?

时间:2018-02-21 22:39:07

标签: laravel phpunit mockery

测试时:

我想查看我网站上的商品,模拟确认,然后继续处理订单的其余部分。

我如何换出模拟的好代码?如:

$gateway = $checkout->getGateway($params['payment_method']);
$response = $gateway->completePurchase($params)->send();

这很容易吗?

基本方向非常感谢,虽然我已经创建了测试,但我在模拟领域的知识是基本的

enter image description here

2 个答案:

答案 0 :(得分:3)

就模拟而言,您不需要知道确切的响应,您只需要知道输入和输出数据,就应该在laravel服务提供者中替换服务(在这种情况下为Paypal)。您需要像下面这样一些步骤: 首先向您的laravel服务提供商添加PaymentProvider

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);
        });
    }

    ...
}

然后在测试类中,应使用该接口的模拟版本替换提供程序:

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());
   }
}

答案 1 :(得分:0)

当我必须模拟包含对外部库的调用的方法(例如您的情况下的Omnipay)时,通常使用这种方法。

您的代码段不是很广泛,但是我假设您的课程看起来像这样:

class PaymentProvider
{
    public function pay($request)
    {
        $gateway = Omnipay::create('paypal');
        $response = $gateway->purchase($request['params'])->send();
        if ($response->isSuccessful()) {
                // do more stuff
        }
    }
}

我要做的是重构类,以便对外部库的调用位于单独的方法内:

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
        }
    }
}

然后,在此重构之后,在测试类中,我们可以利用PHPunit的getMockBuilder给我们带来的多种可能性:

<?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
    }
}

对正在发生的事情的一些解释:

$this->paymentProvider = $this->getMockBuilder(\PaymentProvider::class)
    ->setMethods(['pay'])
    ->getMock();

这为我们提供了Payment类的模拟实例,对于该实例,pay是一个“真实”方法,其实际代码将被实际执行,而所有其他方法(在我们的情况下为{{1 }}是我们关心的对象)是可以覆盖其返回值的存根。

以同样的方式,这里我们在模拟响应类,以便我们可以控制其行为并影响purchaseThroughOmnipay方法的流程:

pay

这里的区别是我们没有调用$omnipayResponse = $this->getMockBuilder(<fully qualified name of the Omnipay response class>::class) ->getMock(); $omnipayResponse->expects($this->once()) ->method('isSuccessful') ->willReturn(true); ,这意味着 all 此类的方法将是存根,我们可以为其覆盖返回值(这正是我们正在为setMethods做)。 当然,如果在isSuccessful方法中调用了更多此类的方法(大概在pay之后),那么您可能不得不多次使用if