如何对Roslyn诊断进行单元测试?

11

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

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

3个回答

17
使用“诊断和代码修复”模板创建新解决方案是一个好的起点。这将创建一个单元测试项目,其中包含几个类,可以让您非常容易地测试诊断功能。
然而,它也显示了它的弱点:这些类在您的代码库中是硬编码的,不是您可以轻松更新的依赖项。在像Roslyn这样不断变化的代码库中,这意味着您会很快落后:测试类针对Beta-1设计,而此时Roslyn已经到达RC2。
我提出了两个解决方案:
  1. 请仔细阅读本文的其余部分,我将概述这些课程正在做什么以及它们的关键方面。之后,您可以根据自己的需求创建自己的实现。

  2. 删除所有这些类,改用我基于这些帮助程序创建的RoslynTester NuGet软件包。这将使您能够立即开始使用RC2版本的Roslyn并更轻松地进行更新。有关更多信息,请查看我的博客Github页面


这个想法

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

对于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仓库我关于编写自己诊断工具的博客文章

1
使用模板项目提供的默认基础设施测试分析器相当复杂。 Dustin Campbell 创建了一个名为RoslynNUnitLight的不错项目,使得测试Roslyn分析器、代码修复和重构变得超级简单。不幸的是,它已经不再维护了。我创建了一个分支并进行了一些调整,例如:
  • 删除了单元测试框架的依赖关系。现在您可以使用自己喜欢的框架(xunit、nunit、mstest等)创建测试。
  • 添加了通过行号定位诊断位置的功能
  • 添加了测试CompletionProviders的基础设施
  • 改进了错误消息
  • 在调试模式下使用差异工具呈现代码差异
这个分支叫做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


1
这里有来自 Microsoft 的 Microsoft.CodeAnalysis.Testing (GitHub) 包, 目前在 MyGet 上提供预发布版本。这些包还包括在 Visual Studio 16.6 模板 Analyzer with Code Fix (.NET Standard) 中。
要使用这些包,您需要将它们的 包源 添加到 NuGet 配置文件 中: 前往 /configuration/packageSources 并添加:
<add key="roslyn-analyzers" value="https://dotnet.myget.org/F/roslyn-analyzers/api/v3/index.json" />

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

在使用NuGet包管理器添加/更新这些包时,您可能需要包括预发布版本

这些包支持测试DiagnosticAnalyzerCodeFixProviderCodeRefactoringProvider, 使用C#Visual Basic编写, 通过添加特定的PackageReference, 可以通过MSTest V2NUnitxUnit.net进行测试:

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

目前不支持测试CompletionProvider

警告: Visual Studio 16.6 模板存在错误,无法编译。 请参考文档获取正确的名称空间和方法名称。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接