4

我们的目的是生成一个字符串输出,该输出需要遵守一组特定的语法规则。我创建了一个对象模型,以便通过 C# 的强类型强制执行该语法,以防止生成无效输出的可能性。

我可以创建积极的测试,即有效的 C# 生成有效的输出。我无法做的是运行否定测试,即确保尝试生成无效输出会在编译时引发错误。

显式示例:

namespace Abstract
{
    public interface FooType { }
    public interface FooString : FooType { }
}

public interface Integer : Abstract.FooType { }    
public interface SingleLine : Abstract.FooString { }
public interface MultiLine : Abstract.FooString { }

public class Bar<T>
    where T : Abstract.FooType
{
    public Bar(string s) {
        // do stuff with s and T, where T is SingleLine or MultiLine
    }

    public Bar(int i) {
        // do stuff with i and T, where T is Integer
    }
}

public static class Foo
{
    public static Bar<T> Bar<T>(int i) where T : Integer {
        return new Bar<T>(i);
    }

    public static Bar<SingleLine> Bar(string s) {
        return new Bar<SingleLine>(s);
    }

    public static Bar<T> Bar<T>(string s) where T : Abstract.FooString {
        return new Bar<T>(s);
    }
}

所有这些只是为了让我能做到:

Foo.Bar<SingleLine>("some string");  // ok
Foo.Bar("another string");           // ok
Foo.Bar<MultiLine>("more\nstrings"); // still ok
Foo.Bar<Integer>(24)                 // also ok

// How to test these lines for compilation failure?
Foo.Bar<Integer>("no good");
Foo.Bar<MultiLine>(-1);

万一这很重要,我正在使用 VS2012 Express for Desktop。

4

4 回答 4

4

我会高度怀疑这样做的代码。但是,如果您想创建一个单元测试以确保有人没有更改代码以允许类以某种方式工作(即在以某种方式使用时停止出​​现编译错误,您可以在此使用 CodeDOM方式:

CSharpCodeProvider codeProvider = new CSharpCodeProvider();
ICodeCompiler icc = codeProvider.CreateCompiler();
CompilerParameters parameters = new CompilerParameters();
parameters.ReferencedAssemblies.Add("mydll.dll");

parameters.GenerateExecutable = false;

CompilerResults results = 
    icc.CompileAssemblyFromSource(parameters, 
        String.Format(@"using System;

namespace Testing
{{
    class Program
    {{
        static void Main(string[] args)
        {{
            {0}
            Console.ReadLine();
        }}
    }}
}}
", "Foo.Bar<Integer>("no good");"));
Assert.AreNotEqual(0, results.Errors.Count);

您基本上创建了一个提供程序,告诉它您要引用特定的 DLL(大概在哪里),然后创建您要测试Foo的代码(文本-注意我将大括号加倍,因为它们是 中的分隔符),包装String.Format在一个类中(包括Main如果你正在生成一个 exe)然后编译文本。Errors您可以验证集合是否发生错误。

于 2012-09-24T13:53:14.663 回答
3

当然可以,只需制作您希望编译失败的代码片段(作为字符串,或将 .cs 文件加载到字符串中)并使用CodeDom在其上调用 C# 编译器。然后您的测试只需要检查编译器是否失败,如果您喜欢检查行号错误消息等...是否正确。

当然,这是一个相当大的努力——你需要评估这真的会给你带来多少收益。如果您正在开发其他开发人员将要使用的某种 API,并且这是一个重要的功能,将来可能会因一些细微的变化而无意中破坏,那么您可能需要对此进行单元测试。否则,这可能需要付出很多努力才能获得很少的回报(IMO)。

于 2012-09-24T13:32:16.613 回答
0

您可能已经注意到,要运行单元测试,必须成功编译其代码,所以这有点像鸡蛋问题。

您可以通过使用反射调用 API 来解决此问题。如果您以不兼容的方式调用类型(例如,当您使用类型约束时),CLR 将引发异常。

另一种选择是在单元测试中使用一段文本来使用Code Dom (或仅触发 csc.exe)。您可以通过让编译失败来使您的测试通过。

于 2012-09-24T13:30:39.850 回答
0

罗斯林会让你做这样的事情。它可能会内置到 .net 5 或 C# 6 或其他东西中。它本质上等同于代码 dom。

于 2012-09-24T13:58:39.613 回答