首页 > 解决方案 > 带有构造函数参数的 PHP 依赖注入使测试变得困难

问题描述

我正在尝试处理一些依赖注入最佳实践。我正在尝试测试使用braintree/braintree_php作曲家库的服务类。假设我有这样的BraintreeService课程:

namespace App\Service;

class BraintreeService
{
    /* @var Braintree_Gateway */
    private $gateway;

    public function __construct()
    {
        $this->gateway = new \Braintree_Gateway([
            'environment' => BRAINTREE_ENVIRONMENT,
            'merchantId' => BRAINTREE_MERCHANT_ID,
            'publicKey' => BRAINTREE_PUBLIC_KEY,
            'privateKey' => BRAINTREE_PRIVATE_KEY,
        ]);
    }

    /*
     * Generate a new client token from Braintree
     *
     * @return string $string
     */
    public function getClientToken() : string
    {
        return $this->gateway->clientToken()->generate();
    }
}

该类的用法将如下所示:

$btService = new \App\Service\BraintreeService();
$token = $btService->getClientToken();

明显的问题是这个服务类是紧密依赖的Braintree_Gateway。因此,使单元测试BraintreeService变得困难。为了使测试更容易,我想转向Braintree_Gateway构造函数参数。这将允许我Braintree_Gateway在单元测试中模拟课程。

但是,据我了解,如果我这样做,那么代码将如下所示:

namespace App\Service;

class BraintreeService
{
    /* @var Braintree_Gateway */
    private $gateway;

    public function __construct(Braintree_Gateway $gateway)
    {
        $this->gateway = $gateway;
    }

    /*
     * Generate a new client token from Braintree
     *
     * @return string $string
     */
    public function getClientToken() : string
    {
        return $this->gateway->clientToken()->generate();
    }
}

该类的用法将如下所示:

$btService = new \App\Service\BraintreeService(
    new \Braintree_Gateway([
        'environment' => BRAINTREE_ENVIRONMENT,
        'merchantId' => BRAINTREE_MERCHANT_ID,
        'publicKey' => BRAINTREE_PUBLIC_KEY,
        'privateKey' => BRAINTREE_PRIVATE_KEY,
    ])
);
$token = $btService->getClientToken();

如果我在整个代码中的多个位置使用此服务,我觉得这会变得很麻烦。我想要一些关于如何更好地处理依赖关系同时仍然能够完全测试我的服务类的建议。谢谢!

标签: phpdependency-injectionmockingphpunit

解决方案


在构造函数中接受 null 作为默认值:

public function __construct(Braintree_Gateway $gateway=null) { 
  if(!gateway) { 
    $gateway = new \Braintree_Gateway([
    'environment' => BRAINTREE_ENVIRONMENT,
    'merchantId' => BRAINTREE_MERCHANT_ID,
    'publicKey' => BRAINTREE_PUBLIC_KEY,
    'privateKey' => BRAINTREE_PRIVATE_KEY,
    ]);
  }
}

这样,您可以覆盖它进行测试,但在生产中保持向后兼容


推荐阅读