简体   繁体   English

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

[英]How can I unit test Roslyn diagnostics?

How can I unit test my own custom analyzers and Code Fix providers? 如何对我自己的自定义分析器和代码修复提供程序进行单元测试?

I'm sitting in front of my computer with my hands on the keyboard but I don't know what to type. 我坐在电脑前,双手放在键盘上,但我不知道要输入什么。

A good place to start is by creating a new solution using the "Diagnostics and Code Fix" template. 一个好的起点是使用“诊断和代码修复”模板创建新的解决方案。 This will create a unit test project that comes with a few classes which allow you to very easily test your diagnostics. 这将创建一个单元测试项目,其中包含几个类,使您可以非常轻松地测试诊断。

However this also shows its weakness: the classes are hardcoded in your codebase and are not a dependency which you can easily update when needed. 然而,这也显示了它的弱点:类在您的代码库中是硬编码的,并且不是您可以在需要时轻松更新的依赖项。 In a still constantly changing codebase like Roslyn, this means you will fall behind quickly: the test classes are aimed at Beta-1 while Roslyn is already at RC2 at the time of writing. 在像Roslyn这样仍在不断变化的代码库中,这意味着你将很快落后:测试类针对的是Beta-1,而Roslyn在撰写本文时已经在RC2。

There are two solutions I propose: 我建议有两种解决方案:

  1. Read through the rest of this post where I give a broad layout of what is being done in those classes and what their key aspects are. 仔细阅读本文的其余部分,我将对这些课程中的内容以及它们的主要方面进行广泛的布局。 Afterwards you can create your own implementation according to your needs. 之后,您可以根据需要创建自己的实现。

  2. Remove all those classes and instead use the RoslynTester NuGet package which I created based on these helpers. 删除所有这些类,而不是使用我基于这些帮助程序创建的RoslynTester NuGet包。 This will allow you to immediately get started with the RC2 version of Roslyn and keep it more easily updated. 这将使您立即开始使用RC2版本的Roslyn并使其更容易更新。 For more information, take a look at my blog or the Github page . 有关更多信息,请查看我的博客Github页面


The idea 这个想法

The idea behind the helpers is simple: given one or more strings that represent class files and one or more objects that represent expected diagnostic results, create an in-memory project with the given classes and execute the analyzers. 帮助程序背后的想法很简单:给定一个或多个表示类文件的字符串和一个或多个表示预期诊断结果的对象,创建具有给定类的内存中项目并执行分析程序。

In the case of a CodeFix provider, you can also specify how the code should look after it's transformed. 对于CodeFix提供程序,您还可以指定代码在转换后的外观。

The execution 执行

How is it called? 怎么称呼?

This is an example test that shows a warning when you have an asynchronous method whose name doesn't end with "Async" and provides a CodeFix to change the name. 这是一个示例测试,当您有一个名称不以“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);
}

As you can see the setup is very straightforward: you determine how the faulty code looks, you specify how it should look and you indicate the properties of the warning that should be displayed. 正如您所看到的,设置非常简单:您可以确定错误代码的外观,指定它的外观,并指出应显示的警告的属性。

Creating the project 创建项目

The first step is to create the in-memory project. 第一步是创建内存中项目。 This consists of a few steps: 这包括几个步骤:

  • Create the workspace ( new AdhocWorkspace() ) 创建工作区( new AdhocWorkspace()
  • Add a new project to it ( .CurrentSolution.AddProject() ) 添加一个新项目( .CurrentSolution.AddProject()
  • Add references to relevant assemblies ( .AddMetadataReferences() ) 添加对相关程序集的引用( .AddMetadataReferences()
  • Add documents to the solution( solution.AddDocument() ) 将文档添加到解决方案( solution.AddDocument()

Gathering the diagnostics 收集诊断信息

Here we will use the documents we just created. 在这里,我们将使用刚刚创建的文档。 These two lines are most important: 这两行最重要:

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

Verifying the diagnostics 验证诊断

At this point you have everything you need: you have the actual results and you have the expected results. 此时,您拥有所需的一切:您拥有实际结果并获得预期结果。 All that is left is verifying that the two collections match. 剩下的就是验证两个集合是否匹配。

Applying a Code Fix 应用代码修复

This roughly follows the same pattern as the diagnostics but adds a little bit to it. 这大致遵循与诊断相同的模式,但增加了一点点。

  • Create a document 创建一个文档
  • Get the analyzer 获取分析仪
  • Create a CodeFixContext 创建CodeFixContext
var actions = new List<CodeAction>();
var context = new CodeFixContext(document, analyzerDiagnostics[0], 
              (a, d) => actions.Add(a), CancellationToken.None);
codeFixProvider.RegisterCodeFixesAsync(context).Wait();
  • Apply the Code Fix 应用代码修复
var operations = codeAction.GetOperationsAsync(CancellationToken.None).Result;
var solution = operations.OfType<ApplyChangesOperation>().Single().ChangedSolution;
  • Optionally: verify no new diagnostics have been triggered due to your refactor (可选):验证由于您的重构没有触发新的诊断
  • Verify the expected source and the resulting source are the same 验证预期的源和结果源是否相同

If everything is still a little blurry, definitely take a look at the exact source code . 如果一切仍然有点模糊,那么一定要看一下确切的源代码 If you want clearer examples of how to create your own diagnostics, take a look at my VSDiagnostics repository or my blogpost on writing your own . 如果您想要更清楚地了解如何创建自己的诊断程序,请在编写自己的 VSDiagnostics存储库我的博客文章时查看

Testing analyzers with the default infrastructure provided by the template project is quite complicated. 使用模板项目提供的默认基础结构测试分析器非常复杂。 There is a nice project RoslynNUnitLight create by Dustin Campbell which makes testing of Roslyn analyzers, code fixes and refactorings super easy. 有一个很好的项目RoslynNUnitLight由Dustin Campbell创建,它使Roslyn分析器,代码修复和重构的测试非常容易。 Unfortunately, it's no longer maintained. 不幸的是,它已不再维护。 I created a fork and made a few adjustments, like: 我创建了一个fork并进行了一些调整,例如:

  • Removed dependencies from the unit test framework. 从单元测试框架中删除了依赖项。 Now you can create a test with your favorite framework: xunit, nunit, mstest, etc. 现在,您可以使用自己喜欢的框架创建测试:xunit,nunit,mstest等。
  • Added ability to locate diagnostic position by the line number 添加了通过行号定位诊断位置的功能
  • Added infrastructure for testing CompletionProviders 添加了用于测试CompletionProviders的基础架构
  • Improved error messages 改进了错误消息
  • Presents code diff using diff tool in debug mode 在调试模式下使用diff工具呈现代码差异

This fork is called RoslynTestKit and it's available on GitHub https://github.com/cezarypiatek/RoslynTestKit 这个fork叫做RoslynTestKit,可以在GitHub上找到https://github.com/cezarypiatek/RoslynTestKit

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

You can find sample tests built with RoslynTestKit in this project https://github.com/smartanalyzers/MultithreadingAnalyzer/tree/master/src/MultithreadingAnalyzer.Test 您可以在此项目中找到使用RoslynTestKit构建的样本测试https://github.com/smartanalyzers/MultithreadingAnalyzer/tree/master/src/MultithreadingAnalyzer.Test

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM