Source Generator 单元测试

Source Generator 单元测试

Intro

Source Generator 是 .NET 5.0 以后引入的一个在编译期间动态生成代码的一个机制,介绍可以参考 C# 强大的新特性 Source Generator

GetStarted

使用起来还算比较简单的,我平时一般用 xunit,所以下面的示例也是使用 xunit 来写单元测试,微软提供的测试组件也有针对 MsTest 和 NUnit 的,可以根据自己需要进行选择 https://www.nuget.org/packages?q=Microsoft.CodeAnalysis.CSharp.SourceGenerators.Testing

3352c7c56a4d1b688796a54816a831ea.png

我的项目是 xunit , 所以首先需要在测试项目中引用 Microsoft.CodeAnalysis.CSharp.SourceGenerators.Testing.XUnit 这个 NuGet 包,如果不是 xunit 选择对应的 NuGet 包即可

如果在还原包的时候有包版本的警告可以显式指定对应包的版本来消除警告

Sample1

首先来看一个最简单的 Source Generator 示例

[Generator]
public class HelloGenerator : ISourceGenerator
{public void Initialize(GeneratorInitializationContext context){// for debugging// if (!Debugger.IsAttached) Debugger.Launch();}public void Execute(GeneratorExecutionContext context){var code = @"namespace HelloGenerated
{public class HelloGenerator{public static void Test() => System.Console.WriteLine(""Hello Generator"");}
}";context.AddSource(nameof(HelloGenerator), code);}
}

这个 Source Generator 就是一个比较简单的生成一个 HelloGenerator 的类,这个类里只有一个 Test 的静态方法,单元测试方法如下:

[Fact]
public async Task HelloGeneratorTest()
{var code = string.Empty;var generatedCode = @"namespace HelloGenerated
{public class HelloGenerator{public static void Test() => System.Console.WriteLine(""Hello Generator"");}
}";var tester = new CSharpSourceGeneratorTest<HelloGenerator, XUnitVerifier>(){TestState ={Sources = { code },GeneratedSources ={(typeof(HelloGenerator), $"{nameof(HelloGenerator)}.cs", SourceText.From(generatedCode, Encoding.UTF8)),}},};await tester.RunAsync();
}

通常来说 Source Generator 的测试分为两部分,一部分是源代码,一部分 Generator 生成的代码

而这个示例比较简单,其实和源代码没有关系,可以没有源代码,上面是给了一个空,也可以不配置 Sources

而 Generated Sources 则是由我们的 Generator 生成的代码

首先我们需要创建一个 CSharpSourceGeneratorTest 有两个泛型类型,第一个是 Generator 类型,第二个是验证器,这和你使用哪个测试框架有关系,xunit 就固定是 XUnitVerifier ,在 test 中指定 TestState 中的源代码和生成的源代码,之后调用 RunAsync 方法就可以了

上面有一个生成的示例,

第一个参数是 Generator 的类型,会根据 Generator 的类型获取生成代码的位置,

第二个参数是在 Generator 里 AddSource 时指定的名称,但是这里需要注意的是,即使指定的名称不是 .cs 结尾的需要也需要在这里添加 .cs 后缀,这个地方感觉可以优化一下,自动加 .cs 后缀

第三个参数就是实际生成的代码了

Sample2

接着我们来看一个稍微复杂一些的,和源代码有关系并且有依赖项

Generator 定义如下:

[Generator]
public class ModelGenerator : ISourceGenerator
{public void Initialize(GeneratorInitializationContext context){// Debugger.Launch();context.RegisterForSyntaxNotifications(() => new CustomSyntaxReceiver());}public void Execute(GeneratorExecutionContext context){var codeBuilder = new StringBuilder(@"
using System;
using WeihanLi.Extensions;namespace Generated
{public class ModelGenerator{public static void Test(){Console.WriteLine(""-- ModelGenerator --"");
");if (context.SyntaxReceiver is CustomSyntaxReceiver syntaxReceiver){foreach (var model in syntaxReceiver.Models){codeBuilder.AppendLine($@"      ""{model.Identifier.ValueText} Generated"".Dump();");}}codeBuilder.AppendLine("    }");codeBuilder.AppendLine("  }");codeBuilder.AppendLine("}");var code = codeBuilder.ToString();context.AddSource(nameof(ModelGenerator), code);}
}internal class CustomSyntaxReceiver : ISyntaxReceiver
{public List<ClassDeclarationSyntax> Models { get; } = new();public void OnVisitSyntaxNode(SyntaxNode syntaxNode){if (syntaxNode is ClassDeclarationSyntax classDeclarationSyntax){Models.Add(classDeclarationSyntax);}}
}

单元测试方法如下:

[Fact]public async Task ModelGeneratorTest(){var code = @"
public class TestModel123{}
";var generatedCode = @"
using System;
using WeihanLi.Extensions;namespace Generated
{public class ModelGenerator{public static void Test(){Console.WriteLine(""-- ModelGenerator --"");""TestModel123 Generated"".Dump();}}
}
";var tester = new CSharpSourceGeneratorTest<ModelGenerator, XUnitVerifier>(){TestState ={Sources = { code },GeneratedSources ={(typeof(ModelGenerator), $"{nameof(ModelGenerator)}.cs", SourceText.From(generatedCode, Encoding.UTF8)),}},};// references// TestState.AdditionalReferencestester.TestState.AdditionalReferences.Add(typeof(DependencyResolver).Assembly);// ReferenceAssemblies//    WithAssemblies//tester.ReferenceAssemblies = tester.ReferenceAssemblies//    .WithAssemblies(ImmutableArray.Create(new[] { typeof(DependencyResolver).Assembly.Location.Replace(".dll", "", System.StringComparison.OrdinalIgnoreCase) }))//    ;//    WithPackages//tester.ReferenceAssemblies = tester.ReferenceAssemblies//    .WithPackages(ImmutableArray.Create(new PackageIdentity[] { new PackageIdentity("WeihanLi.Common", "1.0.46") }))//    ;await tester.RunAsync();}

大体上和前面的示例差不多,比较大的差异在于,这里需要处理依赖项,上面代码中提供的三种处理方式,其中 WithPackages 方式只支持 NuGet 包方式,如果是直接引用的 dll 可以使用前面两种方式来实现

More

在之前的介绍文章中我们推荐在代码里添加一句 Debugger.Launch() 来调试 Source Generator,而有了单元测试之后,我们就可以不需要这个了,debug 我们的测试用例也可以调试我们的 Generator,很多时候就会比较方便,也不需要编译的时候触发选择 Debugger 了会更加高效一些,代码里可以少一些神奇的 Debugger.Launch() 了,更加推荐使用单元测试的方式来测试 Generator

上面的第二个示例依赖项的处理,踩了好多坑,自己试了好多次都不行,Google/StackOverflow 大法好

除了上面的 WithXxx 方式,我们还可以用 AddXxx 方式,Add 是增量的方式,而 With 是完全的替换掉对应的依赖

如果你的项目里也有用到 Source Generator,不妨试一下,上面示例的代码可以从 Github 上获取:https://github.com/WeihanLi/SamplesInPractice/blob/master/SourceGeneratorSample/SourceGeneratorTest/GeneratorTest.cs

References

  • https://stackoverflow.com/questions/65550409/adding-reference-assemblies-to-roslyn-analyzer-code-fix-unit-tests

  • https://www.thinktecture.com/en/net/roslyn-source-generators-analyzers-code-fixes-testing/

  • https://github.com/dotnet/roslyn/blob/main/docs/features/source-generators.cookbook.md#unit-testing-of-generators

  • https://github.com/dotnet/roslyn-sdk/tree/main/src/Microsoft.CodeAnalysis.Testing

  • https://www.nuget.org/packages/Microsoft.CodeAnalysis.CSharp.SourceGenerators.Testing.XUnit/

  • https://github.com/WeihanLi/SamplesInPractice/blob/master/SourceGeneratorSample/SourceGeneratorTest/GeneratorTest.cs

  • C# 强大的新特性 Source Generator

  • 使用 Source Generator 代替 T4 动态生成代码

  • 使用 Source Generator 自动生成 WEB API

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/296420.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

又一个中国男人荣获巨奖!拿奖拿的手软,却坦言“我对诺奖没有兴趣”...

全世界只有3.14 % 的人关注了爆炸吧知识获得诺奖似乎只是时间问题2020年9月10日&#xff0c;2021年科学突破奖&#xff08; BREAKTHROUGH PRIZES&#xff09;正式公布。来自中国香港的科学家卢煜明获得了生命科学科学突破奖&#xff0c;华人数学家孙崧获得了数学新视野奖。前几…

ffbe攻略站_最终幻想勇气启示录ffbe兵员强化攻略

最终幻想勇气启示录兵员如何强化&#xff1f;兵员强化后有哪些加强&#xff1f;来看看9k9k小编带来的最终幻想勇气启示录ffbe兵员强化攻略。在兵员选栏中&#xff0c;我们可以看到有强化兵员这一选项&#xff0c;在这里面&#xff0c;我们可以选择兵员进行强化&#xff0c;强化…

不止命令行!自定义VS生成事件

前言在VS中打开项目属性&#xff0c;选择“生成事件”选项卡。在“生成前事件命令行”或“生成后事件命令行”文本框中可以输入任何命令提示符或.bat文件中有效的命令&#xff1a;但是&#xff0c;有没有可能执行更丰富的命令呢&#xff1f;生成事件的本质上面设置的“生成事件…

如果你女朋友不让你看她卸妆......

1 如果你女朋友不让你看她卸妆▼2 扫地机器人的正确用法&#xff08;图源网络&#xff0c;侵删&#xff09;▼3 来比个心&#xff08;素材来源网络&#xff0c;侵删&#xff09;▼4 精彩攻防战▼5 那些吃辣条的小学生长大了...▼6 人生的道路上有时候也要回头看看▼7 先礼…

[9月29日的脚本] 枚举SharePoint列表(PowerShell)

脚本下载: SPListEnumerator.zip http://gallery.technet.microsoft.com/scriptcenter/SPListEnumerator-PowerShell-b0ce0b9f 本脚本通过一个“大型”列表或者是文档库来枚举并为相关项提供信息。 在SharePoint&#xff08;2007版和2010版&#xff09;中&#xff0c;我们有一个…

在 ASP.NET Core Web API中使用 Polly 构建弹性容错的微服务

在 ASP.NET Core Web API中使用 Polly 构建弹性容错的微服务https://procodeguide.com/programming/polly-in-aspnet-core/在本文中&#xff0c;我们将了解如何在微服务中实现弹性容错&#xff0c;即在 ASP.NET Core 中使用 Polly 构建弹性微服务&#xff08;Web API&#xff0…

Android开发者指南(29) —— USB Host and Accessory

前言 本章内容为Android开发者指南的 USB章节&#xff0c;译为"USB主从设备"&#xff0c;版本为Android 4.0 r1&#xff0c;翻译来自&#xff1a;"太阳火神的美丽人生"&#xff0c;欢迎访问他的博客&#xff1a;"http://alot.sinaapp.com"&#…

知乎超高赞:都有哪些习惯值得长期坚持?

全世界只有3.14 % 的人关注了爆炸吧知识知乎上有个高赞问题&#xff1a;有哪些值得长期坚持下去就能改变人生的好习惯&#xff1f;其中最高频的回答是读书。随着经历和阅历的增加&#xff0c;越来越多的人清醒的认识到&#xff1a;读书不再是学生时代的事&#xff0c;而是一生的…

mysql 免费前端_MySQL

MySQL这是一个关系型数据库&#xff0c;存在表的概念。结构数据库可以存放多张表&#xff0c;每个表可以存放多个字段&#xff0c;每个字段可以存放多个记录。DOS命令操作数据库PHPstudy使用终端打开数据库第一步&#xff1a;设置-->MySQL工具-->MySQL命令行&#xff1b;…

惊了!日本街头出现透明公厕,竟有人排队抢着上!

全世界只有3.14 % 的人关注了爆炸吧知识最近在日本一个公园内&#xff0c;一间透明的厕所火了&#xff01;厕所为什么是透明的&#xff1f;如何保证私密性&#xff1f;还是一个装置艺术&#xff0c;在哗众取宠&#xff1f;相信你跟我有一样的疑问。其实啊&#xff0c;这是日本财…

Gitflow branch与Docker image tag命名冲突怎么办?

谷歌还是比必应要好用一点。在前公司&#xff0c;我根据主流的git flow 给团队搭建了一套devops流程&#xff0c;运行在 docker & k8s上。在现代devops流程中&#xff0c;一般推荐使用git分支名或者git tag作为镜像的tag名。在实际操作中&#xff0c; 我遇到了一个流程阻塞…

ubuntu 安装 php

按照 Unofficial Ubuntu 6.06 (Dapper Drake) Linux Starter Guide的步骤来 一、安装Apache2 sudo apt-get install apache2 然后在Firefox中打开: http://localhost/ 提示成功 安装完毕后&#xff0c;接下来就要启动Apache了 如果只是用于本地开发&#xff0c;加强安全性&…

测试你的Python 水平----6

为什么80%的码农都做不了架构师&#xff1f;>>> 1、问&#xff1a;简述python中unicode和utf-8的关系&#xff1f;常用的函数有哪些&#xff1f;答&#xff1a;unicode是一个字符编码集合&#xff0c;定义每个字符的唯一编码&#xff1b;utf-8则是unicode的一种实现…

970页绝版资料!初高中数学与竞赛知识点+方法技巧,由苏步青当顾问,众多一线名师共同编写!...

全世界只有3.14 % 的人关注了爆炸吧知如何才能学好数学&#xff1f;我国著名数学家苏步青先生曾说&#xff1a;"要学好数学&#xff0c;方法不外乎打好基础&#xff0c;多做习题&#xff0c;多加思索和分析”。为了帮助大家学好数学&#xff0c;今天&#xff0c;超模君要给…

使用WTMPlus快速搭建发卡网

快点关注我们吧作者介绍不倔强的草莓&#xff0c;从事互联网乞讨行业多年&#xff0c;深入了解乞讨业务。开发过草莓要饭网等系统。对互联网乞讨的运营有一定经验。什么是WTM框架&#xff1f;WTM框架是一款.NET平台下的开源快速开发框架,可以快速、灵活、高效的开发一个基于.NE…

mysql老叶博客_MySQL binlog后面的编号最大是多大?【老叶茶馆公众号】

MySQL binlog后面的编号最大是多大&#xff1f;具体文章请关注微信公众号&#xff1a;izhishuedu 【知数堂】 知数堂版权所有。这里我就不啰嗦了&#xff0c;直接上贴代码&#xff1a;版本&#xff1a;5.7.18mysql-5.7.18/sql/binlog.cc 大约在3310行开始&#xff1a;MAX_LOG_…

如何优雅的当海王……

1 这车怪有个性的▼2 这个胡律师要火啊▼3 被拦下的教师节礼物……▼4 老公”偷情“的证据▼5 这船快得很&#xff0c;沉得快▼6 别耽误孩子&#xff0c;让孩子去跑马拉松吧▼7 这是要电脑养蛊&#xff1f;▼你点的每个赞&#xff0c;我都认真当成了喜欢

Linux Command Line 解析

处理模型 Linux kernel 的启动包括很多组件的初始化和相关配置&#xff0c;这些配置参数一般是通过command line 进行配置的。在进行后续分析之前&#xff0c;先来理解一下command line 的处理模型&#xff1a; 要处理的对象是一个字符串&#xff0c;其中包含了各种配置信息&am…

COSCon'21 参会指南 你想要的这里都有

“ 点击蓝字 / 关注我们 ”| 作者&#xff1a;袁睿斌| 编辑&#xff1a;Corrie| 设计&#xff1a;宋传琪开源社寄语2021年的10月&#xff0c;第六届中国开源年会&#xff08;COSCon21&#xff09;再一次如期召开了。今年的开源寄语&#xff0c;我们能够想到是三个关键词&#x…

为什么说减速带是脑残的设计?

全世界只有3.14 % 的人关注了爆炸吧知识开车的谁没有压过减速带啊&#xff0c;当你以较高车速通过它时&#xff0c;剧烈振动会带给你强烈的刺激&#xff0c;有时候胃都感觉不舒服&#xff0c;加强你的不安全疑虑&#xff0c;促使你减速行驶。简单来说&#xff0c;就是“顶你起来…