我如何对Roslyn诊断程序进行单元测试

本文关键字:单元测试 诊断程序 Roslyn | 更新日期: 2023-09-27 17:50:28

如何对自己的自定义分析器和代码修复提供程序进行单元测试?

我坐在电脑前,双手放在键盘上,但我不知道该打什么。

我如何对Roslyn诊断程序进行单元测试

一个好的开始是使用"Diagnostics and Code Fix"模板创建一个新的解决方案。这将创建一个带有几个类的单元测试项目,这些类允许您非常容易地测试您的诊断。

然而,这也显示了它的弱点:类是硬编码在你的代码库中,并不是一个依赖,你可以很容易地在需要时更新。在像Roslyn这样不断变化的代码库中,这意味着您将很快落后:测试类的目标是Beta-1,而在撰写本文时Roslyn已经处于RC2。

我提出了两个解决方案:

  1. 阅读这篇文章的其余部分,我给出了这些类正在做的事情的大致布局以及它们的关键方面。然后,您可以根据自己的需要创建自己的实现。

  2. 删除所有这些类,而是使用我基于这些助手创建的RoslynTester NuGet包。这将允许您立即开始使用RC2版本的Roslyn,并使其更容易更新。更多信息,请查看我的博客或Github页面。


创意

helper背后的思想很简单:给定一个或多个表示类文件的字符串和一个或多个表示预期诊断结果的对象,用给定的类创建一个内存项目并执行分析器。

在CodeFix提供程序的情况下,您还可以指定代码转换后的外观。

执行

怎么叫?

这是一个示例测试,当你有一个异步方法的名称不以"Async"结尾时,它会显示一个警告,并提供一个CodeFix来更改名称。

[TestMethod]
public void AsyncMethodWithoutAsyncSuffixAnalyzer_WithAsyncKeywordAndNoSuffix_InvokesWarning()
{
    var original = @"
using System;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
   class MyClass
   {   
       async Task Method()
       {
       }
   }
}";
    var result = @"
using System;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
   class MyClass
   {   
       async Task MethodAsync()
       {
       }
   }
}";
    var expectedDiagnostic = new DiagnosticResult
    {
        Id = AsyncMethodWithoutAsyncSuffixAnalyzer.DiagnosticId,
        Message = string.Format(AsyncMethodWithoutAsyncSuffixAnalyzer.Message, "Method"),
        Severity = EmptyArgumentExceptionAnalyzer.Severity,
        Locations =
        new[]
        {
            new DiagnosticResultLocation("Test0.cs", 10, 13)
        }
    };
    VerifyCSharpDiagnostic(original, expectedDiagnostic);
    VerifyCSharpFix(original, result);
}

正如您所看到的,设置非常简单:您确定错误代码的外观,您指定它的外观,并指示应该显示的警告属性。

创建项目

第一步是创建内存中的项目。这包括几个步骤:

  • 创建工作区(new AdhocWorkspace())
  • 新建项目(.CurrentSolution.AddProject())
  • 添加相关组件的引用(.AddMetadataReferences())
  • 添加文档到解决方案(solution.AddDocument())

收集诊断信息

这里我们将使用刚刚创建的文档。这两行是最重要的:

var compilation = project.GetCompilationAsync().Result;
var diagnostics = compilation.WithAnalyzers(ImmutableArray.Create(analyzer))
                             .GetAnalyzerDiagnosticsAsync()
                             .Result;

正在验证诊断

在这一点上,你有你需要的一切:你有实际的结果,你有预期的结果。剩下的就是验证两个集合是否匹配。

应用代码修复

大致遵循与诊断相同的模式,但增加了一些内容。

  • 创建文档
  • 获取分析仪
  • 创建CodeFixContext
var actions = new List<CodeAction>();
var context = new CodeFixContext(document, analyzerDiagnostics[0], 
              (a, d) => actions.Add(a), CancellationToken.None);
codeFixProvider.RegisterCodeFixesAsync(context).Wait();
  • 应用代码修复
var operations = codeAction.GetOperationsAsync(CancellationToken.None).Result;
var solution = operations.OfType<ApplyChangesOperation>().Single().ChangedSolution;
  • 可选:验证没有由于重构而触发新的诊断
  • 验证期望源和结果源是相同的

如果一切仍然有点模糊,一定要看一下确切的源代码。如果你想要关于如何创建自己的诊断程序的更清晰的示例,请查看我的VSDiagnostics存储库或我的关于编写自己的诊断程序的博客文章。

使用模板项目提供的默认基础结构测试分析器是相当复杂的。Dustin Campbell创建了一个很棒的项目RoslynNUnitLight,它使Roslyn分析器的测试、代码修复和重构变得超级容易。不幸的是,它不再被维护。我创建了一个分叉并做了一些调整,比如:

    从单元测试框架中移除依赖项。现在你可以用你喜欢的框架创建一个测试:xunit, nunit, mstest等等。
  • 增加了按行号定位诊断位置的功能
  • 添加了测试CompletionProviders的基础设施
  • 改进的错误信息
  • 在调试模式下使用diff工具呈现代码diff

这个分支叫做RoslynTestKit,可以在GitHub上找到https://github.com/cezarypiatek/RoslynTestKit

Nuget包:https://www.nuget.org/packages/SmartAnalyzers.RoslynTestKit/

您可以在本项目中找到使用RoslynTestKit构建的示例测试https://github.com/smartanalyzers/MultithreadingAnalyzer/tree/master/src/MultithreadingAnalyzer.Test

有来自Microsoft的Microsoft. codeanalysis . testing (GitHub)包,当前预发布版本在MyGet上可用。这些也包含在Visual Studio 16.6模板Analyzer with Code Fix()中。净标准) .

为了使用这些包,你必须将它们的包源添加到NuGet配置文件:转到/configuration/packageSources,添加:

<add key="roslyn-analyzers" value="https://dotnet.myget.org/F/roslyn-analyzers/api/v3/index.json" />

如果你没有NuGet配置文件,你可以用dotnet new nugetconfig创建一个新的。

当使用NuGet包管理器添加/更新这些包时,您可能必须包含预发布

这些包支持测试diagnostics analyzer、CodeFixProvider和CodeRefactoringProvider,用 c# Visual Basic编写,通过 MSTest V2 , NUnit xUnit.net ,通过添加特定的PackageReference:

Microsoft.CodeAnalysis.[CSharp|VisualBasic].[Analyzer|CodeFix|CodeRefactoring].Testing.[MSTest|NUnit|XUnit]

目前不支持测试CompletionProvider。

警告: Visual Studio 16.6模板包含错误,无法编译。关于正确的名称空间和方法名,请参阅文档。