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

我正坐在电脑前,手放在键盘上,但我不知道要输入什么。

最佳答案

一个很好的起点是使用“诊断和代码修复”模板创建一个新的解决方案。这将创建一个带有几个类的单元测试项目,使您可以非常轻松地测试诊断程序。

但是,这也显示了它的弱点:这些类在您的代码库中进行了硬编码,并且不是可以在需要时轻松更新的依赖项。在像Roslyn这样仍在不断变化的代码库中,这意味着您将很快落后:测试类针对Beta-1,而在撰写本文时,Roslyn已经针对RC2。

我提出了两种解决方案:

  • 通读本文的其余部分,在那里我对这些类中正在做的事情以及它们的关键方面做了广泛的布局。之后,您可以根据需要创建自己的实现。
  • 删除所有这些类,而使用我根据这些助手创建的RoslynTester NuGet包。这将使您立即开始使用Roslyn的RC2版本,并使其更容易更新。有关更多信息,请查看my blogthe Github page


  • 这个主意

    助手的想法很简单:给定一个或多个代表类文件的字符串和一个或多个代表预期诊断结果的对象,使用给定的类创建一个内存中项目并执行分析器。

    对于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;
    
  • (可选):验证由于重构
  • 而没有触发新的诊断
  • 验证预期的来源和结果来源是否相同


  • 如果一切仍然有些模糊,请绝对看看the exact source code。如果您想获得有关如何创建自己的诊断程序的更清晰示例,请查看my VSDiagnostics repositorymy blogpost on writing your own

    关于c# - 如何对罗斯林诊断程序进行单元测试?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/30384868/

    10-12 02:37