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

Jer*_*vel 9 c# unit-testing roslyn visual-studio-2015

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

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

Jer*_*vel 12

一个好的起点是使用"诊断和代码修复"模板创建新的解决方案.这将创建一个单元测试项目,其中包含几个类,使您可以非常轻松地测试诊断.

然而,这也显示了它的弱点:类在您的代码库中是硬编码的,并且不是您可以在需要时轻松更新的依赖项.在像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);
}
Run Code Online (Sandbox Code Playgroud)

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

创建项目

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

  • 创建工作区(new AdhocWorkspace())
  • 添加一个新项目(.CurrentSolution.AddProject())
  • 添加对相关程序集的引用(.AddMetadataReferences())
  • 将文档添加到解决方案(solution.AddDocument())

收集诊断信息

在这里,我们将使用刚刚创建的文档.这两行最重要:

var compilation = project.GetCompilationAsync().Result;
var diagnostics = compilation.WithAnalyzers(ImmutableArray.Create(analyzer))
                             .GetAnalyzerDiagnosticsAsync()
                             .Result;
Run Code Online (Sandbox Code Playgroud)

验证诊断

此时,您拥有所需的一切:您拥有实际结果并获得预期结果.剩下的就是验证两个集合是否匹配.

应用代码修复

这大致遵循与诊断相同的模式,但增加了一点点.

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

如果一切仍然有点模糊,那么一定要看一下确切的源代码.如果您想要更清楚地了解如何创建自己的诊断程序,请在编写自己的VSDiagnostics存储库我的博客文章时查看.