StackExchange.Precompilation - 如何对预编译诊断进行单元测试? [英] StackExchange.Precompilation - How can I unit test precompilation diagnostics?

查看:95
本文介绍了StackExchange.Precompilation - 如何对预编译诊断进行单元测试?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

背景

我正在使用 StackExchange.Precompilation 在 C# 中实现面向方面的编程.查看我在 GitHub 上的存储库.

I'm using StackExchange.Precompilation to implement aspect-oriented programming in C#. See my repository on GitHub.

基本思想是客户端代码将能够在成员上放置自定义属性,并且预编译器将对具有这些属性的任何成员执行语法转换.一个简单的例子是我创建的 NonNullAttribute.当 NonNullAttribute 放在参数 p 上时,预编译器将插入

The basic idea is that client code will be able to place custom attributes on members, and the precompiler will perform syntax transformations on any members with those attributes. A simple example is the NonNullAttribute I created. When NonNullAttribute is placed on a parameter p, the precompiler will insert

if (Object.Equals(p, null)) throw new ArgumentNullException(nameof(p));

在方法体的开头.

诊断很棒...

我想让错误地使用这些属性变得困难.我发现的最好方法(除了直观的设计)是为无效或不合逻辑的属性使用创建编译时Diagnostic.

I would like to make it difficult to use these attributes incorrectly. The best way I have found (aside from intuitive design) is to create compile-time Diagnostics for invalid or illogical uses of attributes.

例如,NonNullAttribute 在值类型成员上使用没有意义.(即使对于可为空的值类型,因为如果您想保证它们不为空,则应改用不可为空的类型.)创建 Diagnostic 是通知用户的好方法这个错误,不会像异常一样使构建崩溃.

For example, NonNullAttribute does not make sense to use on value-typed members. (Even for nullable value-types, because if you wanted to guarantee they weren't null then a non-nullable type should be used instead.) Creating a Diagnostic is a great way to inform the user of this error, without crashing the build like an exception.

...但是我如何测试它们?

诊断是突出错误的好方法,但我也想确保我的诊断创建代码没有错误.我希望能够设置一个可以预编译这样的代码示例的单元测试

Diagnostics are a great way to highlight errors, but I also want to make sure my diagnostic creating code does not have errors. I would like to be able to set up a unit test that can precompile a code sample like this

public class TestClass {
    public void ShouldCreateDiagnostic([NonNull] int n) { }       
}

并确认创建了正确的诊断(或在某些情况下未创建任何诊断).

and confirm that the correct diagnostic is created (or in some cases that no diagnostics have been created).

任何熟悉 StackExchange.Precompilation 的人都可以给我一些指导吗?

Can anyone familiar with StackExchange.Precompilation give me some guidance on this?

解决方案:

@m0sa 给出的答案非常有帮助.实现有很多细节,所以这里是单元测试的实际样子(使用 NUnit 3).注意using static for SyntaxFactory,这消除了语法树构造中的很多混乱.

The answer given by @m0sa was incredibly helpful. There are a lot of details to the implementation, so here is the unit test actually looks like (using NUnit 3). Note the using static for SyntaxFactory, this removes a lot of clutter in the syntax tree construction.

using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using NUnit.Framework;
using StackExchange.Precompilation;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;

namespace MyPrecompiler.Tests {

    [TestFixture]
    public class NonNull_CompilationDiagnosticsTest {

        [Test]
        public void NonNullAttribute_CreatesDiagnosticIfAppliedToValueTypeParameter() {

            var context = new BeforeCompileContext {
                Compilation = TestCompilation_NonNullOnValueTypeParameter(),
                Diagnostics = new List<Diagnostic>()
            };

            ICompileModule module = new MyPrecompiler.MyModule();

            module.BeforeCompile(context);

            var diagnostic = context.Diagnostics.SingleOrDefault();

            Assert.NotNull(diagnostic);
            Assert.AreEqual("MyPrecompiler: Invalid attribute usage", 
                            diagnostic.Descriptor.Title.ToString()); //Must use ToString() because Title is a LocalizeableString
        }

        //Make sure there are spaces before the member name, parameter names, and parameter types.
        private CSharpCompilation TestCompilation_NonNullOnValueTypeParameter() {
            return CreateCompilation(
                MethodDeclaration(ParseTypeName("void"), Identifier(" TestMethod"))
                .AddParameterListParameters(
                    Parameter(Identifier(" param1"))
                        .WithType(ParseTypeName(" int"))
                        .AddAttributeLists(AttributeList()
                                            .AddAttributes(Attribute(ParseName("NonNull"))))));
        }

        //Make sure to include Using directives
        private CSharpCompilation CreateCompilation(params MemberDeclarationSyntax[] members) {

            return CSharpCompilation.Create("TestAssembly")
               .AddReferences(References)
               .AddSyntaxTrees(CSharpSyntaxTree.Create(CompilationUnit()
                    .AddUsings(UsingDirective(ParseName(" Traction")))
                    .AddMembers(ClassDeclaration(Identifier(" TestClass"))
                                .AddMembers(members))));
        }

        private string runtimePath = @"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6.1\";

        private MetadataReference[] References =>
            new[] {
                MetadataReference.CreateFromFile(runtimePath + "mscorlib.dll"),
                MetadataReference.CreateFromFile(runtimePath + "System.dll"),
                MetadataReference.CreateFromFile(runtimePath + "System.Core.dll"),
                MetadataReference.CreateFromFile(typeof(NonNullAttribute).Assembly.Location)
            };
    }
}

推荐答案

我认为您想在实际发出/编译之前添加诊断,因此步骤是:

I figure you want to add you diagnostics before the actual emit / compilation, so the steps would be:

  1. 创建您的CSharpCompilation,在继续之前确保它没有诊断错误
  2. 创建一个 BeforeCompileContext,并用编译和一个空的 List
  3. 填充它
  4. 创建您的 ICompileModule 的实例并使用步骤 2 中的上下文调用 ICompileModule.BeforeCompile
  5. 检查它是否包含所需的Diagnostic
  1. create your CSharpCompilation, make sure it has no diagnostic errors before going further
  2. create an BeforeCompileContext, and populate it with the compilation and an empty List<Diagnostic>
  3. create an instance of your ICompileModule and call ICompileModule.BeforeCompile with the context from step 2
  4. check that it contains the required Diagnostic

这篇关于StackExchange.Precompilation - 如何对预编译诊断进行单元测试?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆