大家好,我是Edison。
本篇我們基於 的基礎,來寫一個單元測試助手的prompt,讓它幫我們寫一些我們開發者不太願意編寫的單元測試程式碼,進而提高我們的程式碼品質,同時還降低我們的開發工作量。
單元測試助手
這裏我們基於上一篇中提到的萬能語言輔助專家的提示詞,稍作修改,形成我們的單元測試助手的提示詞,如下所示,經過一些測試在GPT4-o模型下效果真的不錯, 建議收藏!
初始回復:「
**Hi I'm Unit Test Master,Created by Edison Zhou,V1.0,20230701**
### ⚙️ Preferences:
- 🌍 lang: <> else C#
- ⏲️ test: <> else xUnit
- 🎯 mock: <> else Moq
### 🤖 Menu:
請使用表格輸出支持的`instructions`和對應名稱,不需要解釋具體含義,也不需要顯示這句話:
---
請指出你的開發語言,E.g.:/lang C#。
回復1保持預設。
」
`preferences`
/lang:<開發語言偏好,預設為C#>
/test:<測試框架偏好,預設為xUnit>
/mock:<模擬框架偏好,預設為Moq>
`instructions`
/models:被測試程式碼涉及到的模型定義
使用者輸入一些被測試程式碼涉及到的Models如Entity,DTO, VO等,請按以下模版輸出Models相關資訊:
## 📝Models:
用表格輸出:**Model Name**
/constants:被測試程式碼涉及到的常量或列舉定義
使用者輸入一些被測試程式碼涉及到的Constants如Enum,Constant等,請按以下模版輸出Constants相關資訊:
## 📝Constants:
用表格輸出:**Constant Name**
/refer: 被測試程式碼依賴的的物件介面定義
使用者輸入一些被測試程式碼涉及到的物件介面如Repository,Gateway等,請按以下模版輸出References相關資訊:
## 📝Refers:
用表格輸出:**Reference Name**
/method:被測試方法的實際程式碼
結合之前的models,constants,refers定義,直接生成被測試方法的單元測試,無需再用表格輸出相關資訊。如果還有其他要求,使用者會補充告訴你。
/help:輸出支持的指令指引
`rules`
- 請使用使用者設定的偏好的開發語言、測試框架和模擬框架實作
- 每個單元測試方法的命名請遵循格式:"被測試方法名_測試場景_預期結果"
- 假如被測試方法中有try-catch,請考慮針對catch部份也編寫單元測試用例
- 請一步一步思考,不需要解釋程式碼,如果有錯誤,使用者會糾正你
對話範例
假設我們有一個基於.NET開發的API計畫,想要對其中某個Service的某個方法寫寫單元測試,我們只需要按照以下步驟即可生成可能會「一把過」的單元測試程式碼。
在我們的實踐中,最好透過VS Code將上述的人設prompt編輯好,同時把下面需要餵給GPT的程式碼片段也準備好。
第一輪:偏好選擇
直接回復1,即保持C#+xUnit+Moq的框架組合,預設偏好設定
第二輪:給GPT餵Models相關 class的定義
例如下所示,如果model有繼承一些介面或基礎類別,最好也一起告訴GPT。
/models
public classAppTokenVo: VoBase, IVo
{
public string TokenName { get; set; }
public AppNamesapce Namespace { get; set; }
public AppTokenType Type { get; set; }
public string Remark { get; set; }
public DateTime ExpireTime { get; set; }
}
public classVoBase : IIdentity<Guid>
{
public virtual Guid Id { get; set; }
}
public classAppTokenEntity : EntityBase, IEntity
{
......
}
......
第三輪:給GPT餵Constants相關 class/enum的定義
例如 下所示, 如果constant有繼承一些介面或基礎類別,最好也一起告訴 GPT。
/constants
publicenumAppStatusCode
{
......
//// +++++++++++++++++++++++++++++++++++++
//// Common error codes [-1..-99]
//// +++++++++++++++++++++++++++++++++++++
UnexpectedException = -1,
OperationFinishedWithErrors = -2,
ValidationFailed = -3,
CouldNotSaveEntityInDb = -4,
EntityNotFoundInDb = -5,
EntityAlreadyExistInDb = -6
}
......
第四輪:給GPT餵被測試方法依賴的一些物件的定義,如Repository或Gateway等
例如 下所示, 如果被依賴的物件有繼承一些介面或基礎類別,最好也一起告訴 GPT。
/refers
public interface IOperationResult<out T> where T : class
{
int StatusCode { get; }
string ErrorMessage { get; }
T Content { get; }
bool HasContent { get; }
bool IsSuccess { get; }
}
public interface IAppTokenRepository : IEfCoreRepositoryBase<AppTokenEntity>
{
}
public interface IEfCoreRepositoryBase<TEntity> where TEntity : EntityBase, IEntity
{
Task<IEnumerable<TEntity>> GetAllAsync(Expression<Func<TEntity, bool>> predicate, bool asNoTracking = true);
Task<TEntity> GetAsync(Guid id, bool asNoTracking = false);
Task<TEntity> GetAsync(Expression<Func<TEntity, bool>> predicate, bool asNoTracking = false);
Task<PagedEntityResult<TEntity>> GetPagedDataAsync(Expression<Func<TEntity, bool>> predicate, int limit, int skip);
Task<PagedEntityResult<TEntity>> GetPagedDataAsync(Expression<Func<TEntity, bool>> predicate, string orderField, bool orderByAsc, int limit, int skip);
TEntity Add(TEntity entity);
TEntity Update(TEntity entity);
TEntity Delete(TEntity entity);
}
第四輪:告訴GPT被測試的方法程式碼
例如 下所示,最好將該service的定義和建構函式也一起告訴GPT,可以將該service的其他方法移除掉再告訴GPT,讓其保持專註。
當然,這也是我們為什麽需要透過編輯器將其編輯好,再統一發給GPT的原因。
/method
public classAppTokenService : AppServiceBase<AppTokenService>, IAppTokenService
{
public AppTokenService(
IAppUnitOfWork unitOfWork,
IMapper mapper,
ILogger<AppTokenService> logger)
: base(unitOfWork, mapper, logger)
{
}
public async Task<IOperationResult<IEnumerable<AppTokenVo>>> GetAllAppTokensAsync()
{
Logger.LogInformation(LoggingConstants.FunctionCalled,
nameof(AppTokenService), nameof(GetAllAppTokensAsync));
try
{
......
return OperationResult<IEnumerable<AppTokenVo>>.Success(appTokens);
}
catch (Exception ex)
{
Logger.LogError(ex, LoggingConstants.Exception,
nameof(AppTokenService), nameof(GetAllAppTokensAsync));
return OperationResult<IEnumerable<AppTokenVo>>.Error((int)AppStatusCode.UnexpectedException);
}
finally
{
Logger.LogInformation(LoggingConstants.FunctionFinished,
nameof(AppTokenService), nameof(GetAllAppTokensAsync));
}
}
}
隨後,GPT就會輸出一些高品質的單元測試程式碼,你可以將其復制出來做一些驗證:
(1) 首先看看有沒有編譯錯誤 ,如果有,那一定是GPT虛構了某些依賴物件的介面定義,換言之,你忘記告訴GPT準確的定義了,因此你需要補充告訴GPT。
(2)其次如果沒有編譯錯誤了,那麽恭喜你,你基本可以得到一個全部Pass的結果。但是, 請再次review一下它的Assert有沒有滿足你的需求 ,如果沒有,請一定反饋給GPT,按照你的需求做一些完善。
(3) 最後建議跑一下程式碼覆蓋率 ,看看還有沒有沒有覆蓋的分支語句和程式碼行,然後反饋給GPT,直到滿足你的需求再結束,比如Line Coverage和Branch Coverage都達到了80%及以上。
最後,你可能會發現你這麽準備下來,可能自己手寫單元測試也差不多寫完了,但是這畢竟是你第一次調教GPT幫你寫單元測試,
當你熟悉這個套路之後,有了自己的固定樣版,以後的單元測試就會越來越快,而且你幾乎不需要寫一行程式碼了
,是不是很酷?
小結
本篇,我們了解了如何基於ChatGPT中的參數化表達溝通,實作一個可以幫我們開發者編寫單元測試程式碼的單元測試助手。當然,我們還可以將其用於其他程式語言和測試框架,只要你願意,你可以根據本文中的樣版去修改和完善。
本文工具
本文範例大模型版本: gpt4-o
參考資料
極客時間,林健,【零基礎GPT套用入門課】:https://time.geekbang.org/column/article/664278
年終總結:
數位化轉型:
C#刷演算法題:
C#刷設計模式:
.NET面試:
.NET大會: