编写单元测试时,一个关键的挑战是确保您的测试专注于被测代码,而不受外部系统或依赖项的干扰。这就是 mock 对象 在 PHPUnit 中发挥作用的地方。它们允许您以受控方式模拟真实对象的行为,使您的测试更可靠且更易于维护。在本文中,我们将探讨什么是模拟对象、它们为何有用以及如何在 PHPUnit 中有效地使用它们。
模拟对象是单元测试中使用的真实对象的模拟版本。它们允许您:
模拟在以下场景中特别有用:
使用模拟对象时,您会遇到两个术语:stubbing 和 mocking:
PHPUnit 通过 createMock() 方法可以轻松创建和使用模拟对象。下面是一些示例,演示了如何有效地使用模拟对象。
在此示例中,我们为类依赖项创建一个模拟对象并指定其行为。
use PHPUnit\Framework\TestCase; class MyTest extends TestCase { public function testMockExample() { // Create a mock for the SomeClass dependency $mock = $this->createMock(SomeClass::class); // Specify that when the someMethod method is called, it returns 'mocked value' $mock->method('someMethod') ->willReturn('mocked value'); // Pass the mock object to the class under test $unitUnderTest = new ClassUnderTest($mock); // Perform the action and assert that the result matches the expected value $result = $unitUnderTest->performAction(); $this->assertEquals('expected result', $result); } }
解释:
有时,您需要验证是否使用正确的参数调用了方法。具体方法如下:
public function testMethodCallVerification() { // Create a mock object $mock = $this->createMock(SomeClass::class); // Expect the someMethod to be called once with 'expected argument' $mock->expects($this->once()) ->method('someMethod') ->with($this->equalTo('expected argument')) ->willReturn('mocked value'); // Pass the mock to the class under test $unitUnderTest = new ClassUnderTest($mock); // Perform an action that calls the mock's method $unitUnderTest->performAction(); }
要点:
为了演示模拟对象的实际应用,我们以依赖于外部 PaymentGateway 接口的 PaymentProcessor 类为例。我们想要测试 PaymentProcessor 的 processPayment 方法,而不依赖于 PaymentGateway 的实际实现。
这是 PaymentProcessor 类:
class PaymentProcessor { private $gateway; public function __construct(PaymentGateway $gateway) { $this->gateway = $gateway; } public function processPayment(float $amount): bool { return $this->gateway->charge($amount); } }
现在,我们可以为 PaymentGateway 创建一个模拟来测试 processPayment 方法,而无需与实际的支付网关交互。
use PHPUnit\Framework\TestCase; class PaymentProcessorTest extends TestCase { public function testProcessPayment() { // Create a mock object for the PaymentGateway interface $gatewayMock = $this->createMock(PaymentGateway::class); // Define the expected behavior of the mock $gatewayMock->method('charge') ->with(100.0) ->willReturn(true); // Inject the mock into the PaymentProcessor $paymentProcessor = new PaymentProcessor($gatewayMock); // Assert that processPayment returns true $this->assertTrue($paymentProcessor->processPayment(100.0)); } }
测试细目:
您还可以验证在处理付款时收费方法是否被调用一次:
public function testProcessPaymentCallsCharge() { $gatewayMock = $this->createMock(PaymentGateway::class); // Expect the charge method to be called once with the argument 100.0 $gatewayMock->expects($this->once()) ->method('charge') ->with(100.0) ->willReturn(true); $paymentProcessor = new PaymentProcessor($gatewayMock); $paymentProcessor->processPayment(100.0); }
在此示例中,expects($this->once()) 确保 charge 方法仅被调用一次。如果该方法未被调用,或者调用多次,测试将失败。
假设您有一个 UserService 类,它依赖于 UserRepository 来获取用户数据。要单独测试 UserService,您可以模拟 UserRepository。
class UserService { private $repository; public function __construct(UserRepository $repository) { $this->repository = $repository; } public function getUserName($id) { $user = $this->repository->find($id); return $user->name; } }
为了测试这个类,我们可以模拟存储库:
use PHPUnit\Framework\TestCase; class UserServiceTest extends TestCase { public function testGetUserName() { // Create a mock for the UserRepository $mockRepo = $this->createMock(UserRepository::class); // Define that the find method should return a user object with a predefined name $mockRepo->method('find') ->willReturn((object) ['name' => 'John Doe']); // Instantiate the UserService with the mock repository $service = new UserService($mockRepo); // Assert that the getUserName method returns 'John Doe' $this->assertEquals('John Doe', $service->getUserName(1)); } }
Mock 对象是在 PHPUnit 中编写单元测试的宝贵工具。它们允许您将代码与外部依赖项隔离,确保您的测试更快、更可靠且更易于维护。模拟对象还有助于验证被测代码及其依赖项之间的交互,确保您的代码在各种场景中都能正确运行
免責聲明: 提供的所有資源部分來自互聯網,如果有侵犯您的版權或其他權益,請說明詳細緣由並提供版權或權益證明然後發到郵箱:[email protected] 我們會在第一時間內為您處理。
Copyright© 2022 湘ICP备2022001581号-3