[架構][技巧] AI UnitTest:讓ChatGPT幫助你編寫更好的測試代碼

2023/05/12閱讀時間約 25 分鐘
在軟體開發領域,AI工具的演進提供新方法來編寫和測試代碼。利用AI來輔助撰寫單元測試已經成為一種新的測試策略。
透過提示AI來撰寫測試代碼,可以更快速地生成測試用例,找出潛在的缺陷並解決問題,從而提高軟體開發的效率和品質。

流程

首先對ChatGPT描述使用的技術架構和想進行的步驟,接著在步驟中和ChatGPT互動並請它提供合適的單元測試代碼。(詳細內容請參考下方的”完整流程”)

優點

採用AI輔助撰寫單元測試的效果比預計還好,特別是測試範圍和效率
前者你可以明確指定AI提供測試的範圍,也可以要求AI提供容易遺漏的測試用例,擴大合理的涵蓋範圍。
後者比起手動輸入測試代碼,透過AI產生測試代碼的速度也會有顯著的提升。
提升速度這一點相當重要,許多排斥撰寫單元測試的開發者,主要原因為在開發時程中很難再排入撰寫單元測試的時間,更不用說當業務邏輯變動之後,與其對應的單元測試也必須重構修改,因此拒絕學習或撰寫單元測試。
若透過AI產生或重構測試代碼,就能大幅降低花費撰寫單元測試的時間,也能順便開啟通往TDD的大道。

缺點

不洩漏敏感訊息的情況下,將想測試的邏輯完整表達給ChatGPT是一個可能的問題,因為我測試的邏輯很簡單,ChatGPT很清楚溫度轉換公式,提供測試代碼相當容易,但若你想測試的演算法或情境特別複雜,要完整的表達清楚就沒這麼簡單了。

總結

在現階段透過AI來輔助撰寫單元測試是一種可以探索的方式,事實上整個過程就是簡單的TDD開發流程,而ChatGPT就是我的結對編程同伴,而且這個同伴很有耐心,有求必應,沒有偏見(?)。
在測試範圍和效率都是AI的優勢,能夠大幅度縮短撰寫測試代碼的時間,但在不洩漏敏感資訊的情況下,如何表達複雜的測試邏輯會是一個可能的問題。

完整流程

以下是透過提示ChatGPT來產生測試代碼,再讓開發者來撰寫產品代碼的過程。
首先告訴ChatGPT系統簡述和技術架構和需求,包含測試庫的要求(MSTest)。
目前的系統是一個溫度轉換程式。
技術架構:C#,Winform,用於模型的測試庫(MSTest)。
所有的程式碼都應該使用上述的技術架構來撰寫。
需求:程式應該提供華氏和攝氏的溫度轉換並以測試驅動開發來撰寫。
請你只提供單元測試,我再回答是否通過你的單元測試,以這種循環方式來完成整個過程

好的,以下是使用 MSTest 撰寫的兩個單元測試。一個用於驗證華氏到攝氏的溫度轉換,另一個用於驗證攝氏到華氏的溫度轉換。
using Microsoft.VisualStudio.TestTools.UnitTesting;
using YourTemperatureConversionNamespace; // 請將此替換為您的溫度轉換類別的命名空間

namespace TemperatureConversionTests
{
[TestClass]
public class TemperatureConversionTest
{
[TestMethod]
public void TestFahrenheitToCelsius()
{
// Arrange
TemperatureConverter converter = new TemperatureConverter();
double inputFahrenheit = 32.0;
double expectedCelsius = 0.0;

// Act
double actualCelsius = converter.FahrenheitToCelsius(inputFahrenheit);

// Assert
Assert.AreEqual(expectedCelsius, actualCelsius, 0.01);
}

[TestMethod]
public void TestCelsiusToFahrenheit()
{
// Arrange
TemperatureConverter converter = new TemperatureConverter();
double inputCelsius = 0.0;
double expectedFahrenheit = 32.0;

// Act
double actualFahrenheit = converter.CelsiusToFahrenheit(inputCelsius);

// Assert
Assert.AreEqual(expectedFahrenheit, actualFahrenheit, 0.01);
}
}
}
這裡假設你有一個 TemperatureConverter 類別,並且該類別有兩個方法:FahrenheitToCelsius 和 CelsiusToFahrenheit。這些方法都接受一個雙精度浮點數(double)並返回一個雙精度浮點數。這些方法應該進行溫度轉換。
請注意,這些測試中的精確度為 0.01,這意味著如果實際結果與預期結果之間的差異小於或等於 0.01,則該測試將被認為是通過的。
請將此單元測試代碼添加到你的測試項目中,並運行這兩個測試。如果你的溫度轉換代碼正確,則這兩個測試都應該通過。
接著就可以建立Winform專案,並在該專案中新增一個TemperatureConversionTest測試類別並直接貼上ChatGPT提供的完整代碼。
當然,執行測試一定會失敗,因為根本還沒有TemperatureConverter類別。
(如果對測試驅動開發有點了解,應該可以發現這也是驅動測試開發的流程,甚至也可以把ChatGPT當作結對編程的對象)
最後再根據ChatGPT提供的單元測試,實作被測代碼。為了避免洩漏敏感訊息,被測代碼不會提供給ChatGPT。
被測代碼如下
public class TemperatureConverter
{
public double FahrenheitToCelsius(double fahrenheit)
{
if (fahrenheit < -459.67)
{
throw new ArgumentOutOfRangeException("The Fahrenheit temperature cannot be less than -459.67");
}
return (fahrenheit - 32) * 5 / 9;
}

public double CelsiusToFahrenheit(double celsius)
{
if (celsius < -273.15)
{
throw new ArgumentOutOfRangeException("The Celsius temperature cannot be less than -273.15");
}
return (celsius * 9 / 5) + 32;
}
}
接著回答ChatGPT,已可通過單元測試,請再提供更多的單元測試。而且在這裡明確指定測試範圍為極限值
我已可通過你提供的單元測試,請再提供更多的單元測試,主要測試範圍為極限值
---
using Microsoft.VisualStudio.TestTools.UnitTesting;
using YourTemperatureConversionNamespace; // 請將此替換為您的溫度轉換類別的命名空間

namespace TemperatureConversionTests
{
[TestClass]
public class TemperatureConversionTest
{
[TestMethod]
public void TestFahrenheitToCelsiusAbsoluteZero()
{
// Arrange
TemperatureConverter converter = new TemperatureConverter();
double inputFahrenheit = -459.67;
double expectedCelsius = -273.15;

// Act
double actualCelsius = converter.FahrenheitToCelsius(inputFahrenheit);

// Assert
Assert.AreEqual(expectedCelsius, actualCelsius, 0.01);
}

[TestMethod]
public void TestCelsiusToFahrenheitAbsoluteZero()
{
// Arrange
TemperatureConverter converter = new TemperatureConverter();
double inputCelsius = -273.15;
double expectedFahrenheit = -459.67;

// Act
double actualFahrenheit = converter.CelsiusToFahrenheit(inputCelsius);

// Assert
Assert.AreEqual(expectedFahrenheit, actualFahrenheit, 0.01);
}

[TestMethod]
public void TestFahrenheitToCelsiusHighTemperature()
{
// Arrange
TemperatureConverter converter = new TemperatureConverter();
double inputFahrenheit = 212.0;
double expectedCelsius = 100.0;

// Act
double actualCelsius = converter.FahrenheitToCelsius(inputFahrenheit);

// Assert
Assert.AreEqual(expectedCelsius, actualCelsius, 0.01);
}

[TestMethod]
public void TestCelsiusToFahrenheitHighTemperature()
{
// Arrange
TemperatureConverter converter = new TemperatureConverter();
double inputCelsius = 100.0;
double expectedFahrenheit = 212.0;

// Act
double actualFahrenheit = converter.CelsiusToFahrenheit(inputCelsius);

// Assert
Assert.AreEqual(expectedFahrenheit, actualFahrenheit, 0.01);
}
}
}
把上述的單元測試新增到TemperatureConversionTest類別,並執行所有的單元測試,被測代碼可以成功通過單元測試!
接下來想對輸入的不合法數值做一些處理,所以要求ChatGPT對溫度低於絕對零度撰寫測試代碼
我已經可以通過你提供的單元測試,但如果輸入的溫度低於絕對零度,我想丟出一個異常,你可以寫出這種測試嗎?

當然可以。在這裡,我將為溫度低於絕對零度的情況寫出兩個測試。我們期望方法在這種情況下拋出一個異常。
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using YourTemperatureConversionNamespace; // 請將此替換為您的溫度轉換類別的命名空間

namespace TemperatureConversionTests
{
[TestClass]
public class TemperatureConversionTest
{
[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void TestFahrenheitToCelsiusBelowAbsoluteZero()
{
// Arrange
TemperatureConverter converter = new TemperatureConverter();
double inputFahrenheit = -500.0; // Less than absolute zero

// Act
converter.FahrenheitToCelsius(inputFahrenheit);

// Assert is handled by the ExpectedException attribute
}

[TestMethod]
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public void TestCelsiusToFahrenheitBelowAbsoluteZero()
{
// Arrange
TemperatureConverter converter = new TemperatureConverter();
double inputCelsius = -300.0; // Less than absolute zero

// Act
converter.CelsiusToFahrenheit(inputCelsius);

// Assert is handled by the ExpectedException attribute
}
}
}
在這兩個測試中,我們使用了 ExpectedException 屬性來指定我們期望方法在給定的情況下拋出的異常類型。在這種情況下,我們期望方法在溫度低於絕對零度時拋出 ArgumentOutOfRangeException。如果你的溫度轉換方法正確地處理了這種情況,那麼這些測試應該會通過。
把ChatGPT新增的測試代碼貼到測試類別之後,執行單元測試應該會失敗。因為在TemperatureConverter沒有根據輸入的不合法數據丟出異常。
回頭修改TemperatureConverter,主要針對華氏低於-459.67和攝氏低於-243.15,丟出異常
public class TemperatureConverter
{
public double FahrenheitToCelsius(double fahrenheit)
{
if (fahrenheit < -459.67)
{
throw new ArgumentOutOfRangeException("The Fahrenheit temperature cannot be less than -459.67");
}
return (fahrenheit - 32) * 5 / 9;
}

public double CelsiusToFahrenheit(double celsius)
{
if (celsius < -273.15)
{
throw new ArgumentOutOfRangeException("The Celsius temperature cannot be less than -273.15");
}
return (celsius * 9 / 5) + 32;
}
}
完成後順利通過單元測試!
最後想請ChatGPT提供它覺得缺乏的單元測試。
我已經可以通過單元測試了,最後我想請提供你覺得缺乏的單元測試

很好,你已經涵蓋了一些重要的測試案例。除了我們已經討論過的測試案例,我們還可以添加一些邊界測試和特殊值測試。以下是一些我們可以添加的額外測試:
  1. 輸入為零的情況:零度華氏對應的攝氏溫度為 -17.78°C,零度攝氏對應的華氏溫度為 32°F。
  2. 一些隨機值的測試:選擇一些隨機的華氏和攝氏溫度值,檢查轉換是否正確。
以下是相應的單元測試:
using Microsoft.VisualStudio.TestTools.UnitTesting;
using YourTemperatureConversionNamespace; // 請將此替換為您的溫度轉換類別的命名空間

namespace TemperatureConversionTests
{
[TestClass]
public class TemperatureConversionTest
{
[TestMethod]
public void TestFahrenheitToCelsiusZero()
{
// Arrange
TemperatureConverter converter = new TemperatureConverter();
double inputFahrenheit = 0.0;
double expectedCelsius = -17.78;

// Act
double actualCelsius = converter.FahrenheitToCelsius(inputFahrenheit);

// Assert
Assert.AreEqual(expectedCelsius, actualCelsius, 0.01);
}

[TestMethod]
public void TestCelsiusToFahrenheitZero()
{
// Arrange
TemperatureConverter converter = new TemperatureConverter();
double inputCelsius = 0.0;
double expectedFahrenheit = 32.0;

// Act
double actualFahrenheit = converter.CelsiusToFahrenheit(inputCelsius);

// Assert
Assert.AreEqual(expectedFahrenheit, actualFahrenheit, 0.01);
}

[TestMethod]
public void TestFahrenheitToCelsiusRandom()
{
// Arrange
TemperatureConverter converter = new TemperatureConverter();
double inputFahrenheit = 45.0; // A random Fahrenheit value
double expectedCelsius = 7.22; // Corresponding Celsius value

// Act
double actualCelsius = converter.FahrenheitToCelsius(inputFahrenheit);

// Assert
Assert.AreEqual(expectedCelsius, actualCelsius, 0.01);
}

[TestMethod]
public void TestCelsiusToFahrenheitRandom()
{
// Arrange
TemperatureConverter converter = new TemperatureConverter();
double inputCelsius = 20.0; // A random Celsius value
double expectedFahrenheit = 68.0; // Corresponding Fahrenheit value

// Act
double actualFahrenheit = converter.CelsiusToFahrenheit(inputCelsius);

// Assert
Assert.AreEqual(expectedFahrenheit, actualFahrenheit, 0.01);
}
}
}
這些測試將增加對你的溫度轉換程式碼的信心。如果所有這些測試都通過了,那麼你的溫度轉換程式碼在這些測試覆蓋的範圍內應該是正確的。請注意,單元測試能幫助我們提高對程式碼的信心,但並不能保證程式碼百分之百的正確。例如,有可能存在我們尚未考慮到的邊界情況或特殊情況。因此,雖然這些測試是一個很好的起點,但可能仍需要進一步的測試和審查來確保程式碼的健壯性。
1會員
6內容數
目前專注於軟體設計架構領域,旨在分享並提供良好且合適的原則 / 模式 / 系統 / 開發法。
留言0
查看全部
發表第一個留言支持創作者!