當前位置: 妍妍網 > 碼農

ChatGPT學習之旅 (8) 單元測試助手

2024-07-09碼農

大家好,我是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。

/modelspublic 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。

/constantspublicenumAppStatusCode{ ......//// +++++++++++++++++++++++++++++++++++++//// Common error codes [-1..-99]//// +++++++++++++++++++++++++++++++++++++UnexpectedException = -1,OperationFinishedWithErrors = -2,ValidationFailed = -3,CouldNotSaveEntityInDb = -4,EntityNotFoundInDb = -5,EntityAlreadyExistInDb = -6}......

第四輪:給GPT餵被測試方法依賴的一些物件的定義,如Repository或Gateway等

例如 下所示, 如果被依賴的物件有繼承一些介面或基礎類別,最好也一起告訴 GPT。

/referspublic 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的原因。

/methodpublic 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大會: