当前位置: 欣欣网 > 码农

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大会: