【问题标题】:How to unit test C# code that's expected to fail compilation for negative testing? [closed]如何对预期无法编译负面测试的 C# 代码进行单元测试? [关闭]
【发布时间】:2012-09-24 13:07:31
【问题描述】:

我们的目的是生成一个字符串输出,该输出需要遵守一组特定的语法规则。我创建了一个对象模型,以便通过 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。

【问题讨论】:

  • 您的意思是,测试特定用途是否被编译器拒绝?
  • 也许您可以在您的问题中添加一个示例,说明这种不可编译的代码可能是什么?
  • 如果您的代码无法编译,您将无法对其进行测试。所以编译器会测试你的代码是否有语法错误。而且您只对运行时部分进行单元测试。
  • @Damien_The_Unbeliever 添加示例
  • 您的示例看起来像是针对 C# 编译器的单元测试,而不是针对您的代码的单元测试。您想通过此测试检测什么样的错误?

标签: c# unit-testing compilation strong-typing


【解决方案1】:

我非常怀疑执行此操作的代码。但是,如果您想创建一个单元测试以确保有人没有更改代码以允许类以某种方式工作(即在以某种方式使用时停止出​​现编译错误,您可以在此使用 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 所在的位置)然后创建代码(文本--注意我将大括号加倍,因为它们是 @ 中的分隔符987654323@)你想测试,包装在一个类中(包括Main,如果你正在生成一个exe)然后编译文本。您可以验证 Errors 集合是否发生错误。

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

    • 是的......关于努力/奖励考虑的好点
    【解决方案3】:

    Roslyn 会让你做这样的事情。它可能会内置到 .net 5 或 C# 6 或其他东西中。不过它本质上等同于 code dom。

    【讨论】:

      【解决方案4】:

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

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

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

      【讨论】:

      • 反射也是鸡蛋里的东西,你需要先编译代码才能进行反射。
      • @PeterRitchie:这取决于。如果您可以检查使用您的 API 的代码是否可以编译,您可以使用反射。当然 API 必须能够编译。
      猜你喜欢
      • 1970-01-01
      • 2010-09-09
      • 2017-04-29
      • 1970-01-01
      • 2010-09-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-09-18
      相关资源
      最近更新 更多