【问题标题】:Testing Stripe in Laravel在 Laravel 中测试条带
【发布时间】:2018-07-13 21:00:26
【问题描述】:

我正在 Laravel 中创建一个基于订阅的 SaaS 平台,其中 Laravel Cashier 不适合我的需求。因此,我需要使用 Stripe 库自己实现订阅引擎。

我发现通过挂钩 Subscription 类的创建和删除事件来实现 Laravel 和 Stripe 之间的连接很容易,然后相应地创建或取消 Stripe 订阅。

不幸的是,Stripe 库主要基于调用一些预定义类的静态方法(.. 像 \Stripe\Charge::create())。

这让我很难测试,因为您通常会允许依赖注入一些自定义客户端进行模拟,但由于 Stripe 库是静态引用的,因此没有客户端可以注入。有什么方法可以创建我可以模拟的 Stripe 客户端类等吗?

【问题讨论】:

  • stripe-php 库已经过很好的测试,也许检查一下they test their HttpClient 可能对您有什么帮助
  • 感谢您的意见。我的问题是测试我是否使用正确的参数调用了 Stripe 库——而不是测试与 Stripe API 本身的连接。
  • 您可以在其上放置一个库并以这种方式对其进行测试,因为该库本身已经过测试。也许这对你来说是一个选择?

标签: php laravel unit-testing stripe-payments


【解决方案1】:

来自未来的你好!

我只是在研究这个。所有这些类都继承自 Stripe 的 ApiResource 类,继续挖掘,你会发现当库即将发出 HTTP 请求时,它会调用 $this->httpClient()httpClient 方法返回对名为$_httpClient 的变量的静态引用。方便的是,Stripe ApiRequestor 类上还有一个名为 setHttpClient 的静态方法,它接受一个假定对象来实现 Stripe HttpClient\ClientInterface(这个接口只描述了一个名为 @ 987654328@)。

Soooooo,在您的测试中,您可以调用 ApiRequestor::setHttpClient,将您自己的 http 客户端模拟实例传递给它。然后,每当 Stripe 发出 HTTP 请求时,它都会使用你的 mock 而不是默认的 CurlClient。然后,您的责任就是让您的模拟返回格式良好的 Stripe 式响应,而您的应用程序将不再明智。

这是我在测试中开始使用的一个非常愚蠢的假货:

<?php

namespace Tests\Doubles;

use Stripe\HttpClient\ClientInterface;

class StripeHttpClientFake implements ClientInterface
{
    private $response;
    private $responseCode;
    private $headers;

    public function __construct($response, $code = 200, $headers = [])
    {
        $this->setResponse($response);
        $this->setResponseCode($code);
        $this->setHeaders($headers);
    }

    /**
     * @param string $method The HTTP method being used
     * @param string $absUrl The URL being requested, including domain and protocol
     * @param array $headers Headers to be used in the request (full strings, not KV pairs)
     * @param array $params KV pairs for parameters. Can be nested for arrays and hashes
     * @param boolean $hasFile Whether or not $params references a file (via an @ prefix or
     *                         CURLFile)
     *
     * @return array An array whose first element is raw request body, second
     *    element is HTTP status code and third array of HTTP headers.
     * @throws \Stripe\Exception\UnexpectedValueException
     * @throws \Stripe\Exception\ApiConnectionException
     */
    public function request($method, $absUrl, $headers, $params, $hasFile)
    {
        return [$this->response, $this->responseCode, $this->headers];
    }

    public function setResponseCode($code)
    {
        $this->responseCode = $code;

        return $this;
    }

    public function setHeaders($headers)
    {
        $this->headers = $headers;

        return $this;
    }

    public function setResponse($response)
    {
        $this->response = file_get_contents(base_path("tests/fixtures/stripe/{$response}.json"));

        return $this;
    }
}

希望这会有所帮助:)

【讨论】:

    【解决方案2】:

    根据 Colin 的回答,这是一个使用模拟接口测试在 Laravel 8.x 中创建订阅的示例。

        /**
         * @test
         */
        public function it_subscribes_to_an_initial_plan()
        {
            $client = \Mockery::mock(ClientInterface::class);
    
            $paymentMethodId = Str::random();
    
            /**
             * Creates initial customer...
             */
            $customerId = 'somecustomerstripeid';
            $client->shouldReceive('request')
                ->withArgs(function ($method, $path, $params, $opts) use ($paymentMethodId) {
                    return $path === "https://api.stripe.com/v1/customers";
                })->andReturn([
                    "{\"id\": \"{$customerId}\" }", 200, []
                ]);
    
    
            /**
             * Retrieves customer
             */
            $client->shouldReceive('request')
                ->withArgs(function ($method, $path, $params) use ($customerId) {
                    return $path === "https://api.stripe.com/v1/customers/{$customerId}";
                })->andReturn([
                    "{\"id\": \"{$customerId}\", \"invoice_settings\": {\"default_payment_method\": \"{$paymentMethodId}\"}}", 200, [],
                ]);
    
            /**
             * Set payment method
             */
            $client->shouldReceive('request')
                ->withArgs(function ($method, $path, $params) use ($paymentMethodId) {
                    return $path === "https://api.stripe.com/v1/payment_methods/{$paymentMethodId}";
                })->andReturn([
                    "{\"id\": \"$paymentMethodId\"}", 200, [],
                ]);
    
            $subscriptionId = Str::random();
    
            $itemId = Str::random();
    
            $productId = Str::random();
    
            $planName = Plan::PROFESSIONAL;
            $plan = Plan::withName($planName);
    
            /**
             *  Subscription request
             */
            $client->shouldReceive('request')
                ->withArgs(function ($method, $path, $params, $opts) use ($paymentMethodId, $plan) {
                    $isSubscriptions = $path === "https://api.stripe.com/v1/subscriptions";
                    $isBasicPrice = $opts["items"][0]["price"] === $plan->stripe_price_id;
    
                    return $isSubscriptions && $isBasicPrice;
                })->andReturn([
                    "{
                    \"object\": \"subscription\",
                    \"id\": \"{$subscriptionId}\",
                    \"status\": \"active\",
                    \"items\": {
                        \"object\": \"list\",
                        \"data\": [
                            {
                                \"id\": \"{$itemId}\",
                                \"price\": {
                                    \"object\": \"price\",
                                    \"id\": \"{$plan->stripe_price_id}\",
                                    \"product\": \"{$productId}\"
                                },
                                \"quantity\": 1
                            }
                        ]
                    }
                    }", 200, [],
                ]);
    
            ApiRequestor::setHttpClient($client);
    
    
            $this->authenticate($this->user);
            $res = $this->putJson('/subscribe', [
                'plan'              => $planName,
                'payment_method_id' => $paymentMethodId,
            ]);
    
            $res->assertSuccessful();
    
            // Actually interesting assertions go here
        }
    

    【讨论】:

      猜你喜欢
      • 2014-10-05
      • 1970-01-01
      • 2019-07-04
      • 2016-10-10
      • 2013-12-28
      • 2019-07-13
      • 2022-10-25
      • 2021-05-21
      • 2015-03-27
      相关资源
      最近更新 更多