【问题标题】:How to mock a paypal transaction in laravel with phpunit?如何使用 phpunit 在 laravel 中模拟贝宝交易?
【发布时间】:2018-08-01 16:09:11
【问题描述】:

测试时:

从我的网站结帐时,需要模拟确认...以便我们可以继续处理订单。可以在哪里进行测试..

如何将好的代码换成模拟代码?如:

$gateway = Omnipay::create('paypal');
$response = $gateway->purchase($request['params'])->send();
if ($response->isSuccessful()) { ... etc ...

这怎么可能?

虽然我创建了测试,但我在模拟领域的知识是基本的

【问题讨论】:

  • 请展示你是如何模拟使用 laravel 和omnipay/paypal 工作的。这样人们就可以在不使用贝宝的情况下测试结帐
  • $checkout 变量是什么?在您的代码 sn-p 中添加更多上下文。
  • 我认为我们需要比这两行更长的 sn-p... 我不熟悉 Paypal 的 PHP SDK,但我对 PHPUnit 非常熟悉。如果您多展示一下您的付款流程,那么它会更容易提供帮助。
  • @HarryBosh 你看到我的回答了吗?

标签: laravel phpunit mockery omnipay


【解决方案1】:

就它依赖于模拟而言,你不需要知道确切的响应,你只需要知道输入和输出数据,你应该在 laravel 服务提供商中替换你的服务(在这种情况下是 Paypal)。您需要以下步骤: 首先将PaymentProvider添加到你的laravel服务提供者:

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

【讨论】:

  • PaypalPackageYourAreUsing 在哪里?
  • 展示你如何使用 laravel 和 omnipay/paypal 进行模拟。
【解决方案2】:

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

你的 sn-p 不是很广泛,但我假设你的类看起来像这样:

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 是一个“真实”方法,其实际代码实际执行,而所有其他方法(在我们的例子中,purchaseThroughOmnipay 是我们care about) 是我们可以覆盖其返回值的存根。

同样,这里我们模拟了响应类,这样我们就可以控制它的行为并影响pay方法的流程:

$omnipayResponse = $this->getMockBuilder(<fully qualified name of the Omnipay response class>::class)
    ->getMock();

$omnipayResponse->expects($this->once())
    ->method('isSuccessful')
    ->willReturn(true);

这里的区别是我们没有调用setMethods,这意味着所有这个类的方法将是我们可以覆盖返回值的存根(这正是我们为isSuccessful 做事。 当然,如果在pay方法中调用了这个类的更多方法(大概在if之后),那么你可能不得不多次使用expect

【讨论】:

    猜你喜欢
    • 2012-08-31
    • 2011-09-17
    • 2015-10-13
    • 1970-01-01
    • 2017-12-08
    • 2016-09-10
    • 2018-04-03
    • 2015-08-11
    • 2017-06-29
    相关资源
    最近更新 更多