在这篇文章中,我们将探讨我在进行自动化软件测试时利用ChatGPT功能的一个工作流程。
如果你对用人工智能提高开发者的生产力感兴趣,你可能会对我的另一篇关于使用ChatGPT和PUML进行软件架构可视化的文章感兴趣。
迭代提示的工作流程
我所使用的一般工作流程包括以下步骤:
-
用有限的信息进行初始提示。
-
审查和完善输出。
-
用固定的输出进行提示,并说明如何进一步改进。
-
ChatGPT根据提供的修复方法生成改进的输出。
-
重复这个过程,直到输出符合你的标准。
这个工作流程的强大之处在于,能够对生成的输出的特定部分提供反馈,并让人工智能模型对整个输出进行改进。
这种方法可以用于不同的应用,例如写作或学习新概念,并反复提出问题以加深理解。
通过迭代提示进行单元测试
让我们深入了解一下单元测试的例子。在我的例子中,我将要求ChatGPT为PaymentOrderProcessor类写单元测试。在这个类中有很多的依赖关系,有一些事情会导致成功或失败。此外,在执行过程中可能会抛出不同类型的异常。
我使用PHP和phpunit进行测试,但是这种方法可以应用于任何单元测试框架或语言。
PaymentOrderProcessor 类的定义如下。我们将为其processPayment 方法编写单元测试。
class PaymentOrderProcessor {
private PaymentOrderRepositoryInterface $repository;
private NotificationSenderInterface $notificationService;
private APIClientInterface $apiClient;
private LoggerInterface $logger;
private FraudDetectorInterface $fraudDetector;
public function __construct(
PaymentOrderRepositoryInterface $repository,
NotificationSenderInterface $notificationService,
APIClientInterface $apiClient,
LoggerInterface $logger,
FraudDetectorInterface $fraudDetector
) {
$this->repository = $repository;
$this->notificationService = $notificationService;
$this->apiClient = $apiClient;
$this->logger = $logger;
$this->fraudDetector = $fraudDetector;
}
public function processPayment(PaymentOrder $paymentOrder): PaymentOrderProcessingResult
{
if ($this->fraudDetector->isSuspicious($paymentOrder)) {
$this->logger->warning("PaymentOrder {$paymentOrder->getId()} marked as suspicious");
$this->notificationService->sendNotification($paymentOrder, false);
return new PaymentOrderProcessingResult(false, "Payment marked as suspicious");
}
$paymentOrderApiResponse = $this->apiClient->sendPaymentOrderRequest($paymentOrder);
if ($paymentOrderApiResponse->isSuccessful() === false) {
throw new PaymentOrderRequestFailure($paymentOrderApiResponse->getMessage());
}
try {
$this->repository->savePaymentOrderResult($paymentOrder, $paymentOrderApiResponse);
} catch (PersistenceException $e) {
$this->logger->critical($e->getMessage());
return new PaymentOrderProcessingResult(false, "Failed to save payment order result");
}
$this->notificationService->sendNotification($paymentOrder, true);
return new PaymentOrderProcessingResult(true);
}
}
提示 1:
我要求进行单元测试,并提供了一个PaymentOrderProcessor类的定义。
为下面这个类写单元测试。类测试的模拟属性名称应该以 "mock "后缀{类定义}结尾。
输出 1:
下面是ChatGPT生成的测试方法的整个主体:
public function testProcessPaymentSuspicious(): void
{
$paymentOrder = new PaymentOrder(1, 100, 'USD');
$this->fraudDetectorMock
->expects($this->once())
->method('isSuspicious')
->with($paymentOrder)
->willReturn(true);
$this->loggerMock
->expects($this->once())
->method('warning')
->with("PaymentOrder 1 marked as suspicious");
$this->notificationServiceMock
->expects($this->once())
->method('sendNotification')
->with($paymentOrder, false);
$processor = new PaymentOrderProcessor(
$this->repositoryMock,
$this->notificationServiceMock,
$this->apiClientMock,
$this->loggerMock,
$this->fraudDetectorMock
);
$result = $processor->processPayment($paymentOrder);
$this->assertInstanceOf(PaymentOrderProcessingResult::class, $result);
$this->assertFalse($result->isSuccess());
$this->assertEquals("Payment marked as suspicious", $result->getMessage());
}
}
这段代码几乎是正确的!
我应用了以下非常简单的修正,删除了不存在的参数和方法的假设。
然后测试就通过了:
第2个提示:
在第二个提示中,鉴于ChatGPT已经解析了PaymentOrderProcessor 的类定义,并为其中一个场景编写了几乎完全正确的测试,我可以继续进行迭代提示。
我将只提供ChatGPT生成的方法的主体,并应用我的简单修正。
我做了一些修正。根据下面固定的例子写更多的测试。包括成功场景
{方法体}。
输出2:
这里有更多的测试,包括成功场景:{其余的测试方法}。
整个输出:
public function testProcessPaymentSuccessful(): void
{
$paymentOrder = new PaymentOrder(1, 100);
$this->fraudDetectorMock
->expects($this->once())
->method('isSuspicious')
->with($paymentOrder)
->willReturn(false);
$paymentOrderApiResponse = new PaymentOrderApiResponse(true);
$this->apiClientMock
->expects($this->once())
->method('sendPaymentOrderRequest')
->with($paymentOrder)
->willReturn($paymentOrderApiResponse);
$this->repositoryMock
->expects($this->once())
->method('savePaymentOrderResult')
->with($paymentOrder, $paymentOrderApiResponse);
$this->notificationServiceMock
->expects($this->once())
->method('sendNotification')
->with($paymentOrder, true);
$processor = new PaymentOrderProcessor(
$this->repositoryMock,
$this->notificationServiceMock,
$this->apiClientMock,
$this->loggerMock,
$this->fraudDetectorMock
);
$result = $processor->processPayment($paymentOrder);
$this->assertInstanceOf(PaymentOrderProcessingResult::class, $result);
$this->assertTrue($result->isSuccessful());
}
public function testProcessPaymentPaymentOrderRequestFailure(): void
{
$paymentOrder = new PaymentOrder(1, 100);
$this->fraudDetectorMock
->expects($this->once())
->method('isSuspicious')
->with($paymentOrder)
->willReturn(false);
$paymentOrderApiResponse = new PaymentOrderApiResponse(false, 'Payment failed');
$this->apiClientMock
->expects($this->once())
->method('sendPaymentOrderRequest')
->with($paymentOrder)
->willReturn($paymentOrderApiResponse);
$this->expectException(PaymentOrderRequestFailure::class);
$this->expectExceptionMessage('Payment failed');
$processor = new PaymentOrderProcessor(
$this->repositoryMock,
$this->notificationServiceMock,
$this->apiClientMock,
$this->loggerMock,
$this->fraudDetectorMock
);
$processor->processPayment($paymentOrder);
}
public function testProcessPaymentPersistenceException(): void
{
$paymentOrder = new PaymentOrder(1, 100);
$this->fraudDetectorMock
->expects($this->once())
->method('isSuspicious')
->with($paymentOrder)
->willReturn(false);
$paymentOrderApiResponse = new PaymentOrderApiResponse(true);
$this->apiClientMock
->expects($this->once())
->method('sendPaymentOrderRequest')
->with($paymentOrder)
->willReturn($paymentOrderApiResponse);
$this->repositoryMock
->expects($this->once())
->method('savePaymentOrderResult')
->with($paymentOrder, $paymentOrderApiResponse)
->willThrowException(new PersistenceException('Failed to save payment order result'));
$this->loggerMock
->expects($this->once())
->method('critical');
$processor = new PaymentOrderProcessor(
$this->repositoryMock,
$this->notificationServiceMock,
$this->apiClientMock,
$this->loggerMock,
$this->fraudDetectorMock
);
$result = $processor->processPayment($paymentOrder);
$this->assertInstanceOf(PaymentOrderProcessingResult::class, $result);
$this->assertFalse($result->isSuccessful());
}
这些测试几乎都是正确的!
ChatGPT不知道PaymentOrderApiResponse 类的第二个参数$message ,因为它在被测试的类中从未被访问。
我应用了以下修正:
现在所有的测试都通过了!
试试吧!
经过一段时间的练习,你会感觉到这种方法对你的各种任务(包括测试或编写代码)的作用,但你可以学会用它来相当快地加速编写单元测试。