当我必须模拟包含对外部库的调用的方法时,这是我通常采用的方法(例如在您的情况下为 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。