我们旨在生成遵守一组特定语法规则所需的字符串输出。我创建了一个对象模型,以便通过 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>("morenstrings"); // 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桌面版。
我非常怀疑这样做的代码。 但是,如果你想创建一个单元测试来确保有人没有更改代码以允许类以某种方式工作(即,在特定方式使用时停止出现编译错误,你可以这样使用 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
集合是否发生了错误。
当然可以,只需制作要编译失败的代码片段(作为字符串,或将.cs文件加载到字符串中),并使用 CodeDom 在其上调用 C# 编译器。 然后,您的测试只需要检查编译器是否失败,如果您想检查行号错误消息等......是正确的。
当然,这是一个相当大的努力 - 你需要评估这真的会给你带来多少收益。 如果您正在开发其他开发人员将使用的某种 API,并且这是一个重要的功能,将来可能会通过一些细微的更改无意中中断,那么您可能需要对此进行单元测试。 否则,这可能会付出很多努力而几乎没有回报(IMO)。
>Roslyn会让你做这样的事情。它可能会内置在.net 5或C# 6或其他东西中。它本质上等同于代码 dom。
您可能已经注意到,要运行单元测试,其代码必须成功编译,因此这是一个先有鸡还是先有蛋的问题。
可以通过使用反射调用 API 来解决此问题。如果以不兼容的方式调用类型(例如,使用类型约束时),CLR 将引发异常。
另一种选择是在单元测试中使用一段文本来使用 Code Dom(或仅触发 csc.exe)。可以通过让编译失败来使测试通过。