是否有一个C#单元测试框架支持任意表达式而不是一组有限的adhoc方法?

Eam*_*nne 23 c# linq unit-testing expression-trees

基本上NUnit,xUnit,MbUnit,MsTest等具有类似于以下的方法:

Assert.IsGreater(a,b)
//or, a little more discoverable
Assert.That(a, Is.GreaterThan(b))
Run Code Online (Sandbox Code Playgroud)

但是,内置的这种比较运算符数量有限; 并且他们不必要地复制语言运算符.当我想要任何稍微复杂的东西时,比如...

Assert.That(a.SequenceEquals(b))
Run Code Online (Sandbox Code Playgroud)

我经常要么继续挖掘手册以找到NUnit-speak中表达式的等价物,要么被迫回退到具有较少有用错误消息的普通布尔断言.

但是,C#与任意表达式很好地集成 - 因此应该可以使用具有以下签名的方法:

void That(Expression<Func<bool>> expr);
Run Code Online (Sandbox Code Playgroud)

这种方法既可用于执行测试(即验证断言),也可用于在测试失败时提供不太透明的诊断; 毕竟,表达式可以呈现为伪代码,以指示哪个表达式失败; 通过一些努力,您甚至可以智能地评估失败的表达式,以提供子表达式值的一些线索.

例如:

Assert.That(()=> a == b);//could inspect expression and print a and b
Assert.That(()=> a < b && b < c);
//could mention the values of "a<b" and "b<c" and/or list the values of a, b, and c.
Run Code Online (Sandbox Code Playgroud)

至少,它会使表达式的并行语言变得不必要,并且在某些情况下,它可能使失败消息更有用.

这样的事情存在吗?

编辑: 在尝试(并且喜欢!)Power Assert之后,我最终重新实现它以解决一些限制.我的变体发表为ExpressionToCode ; 请参阅下面答案以获取改进列表.

Guy*_*Guy 11

查看PowerAssert库(下面的示例输出):

PAssert.IsTrue(() => x + 5 == d.Month * y);


System.Exception : IsTrue failed, expression was:

x + 5 == d.Month * y
| |   |  | |     | |
| |   |  | |     | 6
| |   |  | |     18
| |   |  | 3
| |   |  01/03/2010 00:00:00
| |   False
| 16
11
Run Code Online (Sandbox Code Playgroud)

http://powerassert.codeplex.com/


Cam*_*dan 5

http://satisfyr.codeplex.com/

完全按照您的描述使用lambda表达式.您甚至不需要二进制依赖项,只需添加与您的单元测试框架相对应的单个源文件.


小智 5

实际上有一个很好的理由是NUnit提供自己的DSL而不是使用普通的C#表达式.NUnit应该使用相同的语法处理任何 .NET语言.这并不是说我们不能拥有lambdas,只是因为我们不会完全依赖任何特定的语言功能.

所提出的许多想法都可行,许多第三方软件解决方案可以纳入NUnit,只要他们的作者想要提供它们.当然,很多人喜欢将他们的解决方案分开,这也没关系.但如果您希望他们与NUnit更紧密地合作,请与您最喜欢的作者交谈.

在NUnit 2.5中,您可以使用PredicateConstraint,它以lambda作为参数.但是,语法有点限制.Matches关键字将在表达式的中间工作,因此您可以编写...

Assert.That(someActual,Not.Matches(someLambda);

但这样做没有不要求......

Assert.That(someActual,new PredicateConstraint(someLambda));

当然,这些都不像建议的语法那样干净.

欢迎所有对此问题感兴趣的人加入我们的nunit讨论,讨论NUnit中的内容实际上会导致行动!

查理


Eam*_*nne 2

(原海报在这里)

我喜欢PowerAssert.NET的简单语法和消息,但它生成的 C# 有很多问题。特别是,它不支持多种表达式功能,并且不会在运算符优先级/关联性需要的地方添加括号。在修复了一些错误(并将其报告给作者)后,我发现使用不同的方法修复并从头开始重新实现它会更简单。

用法类似:

PAssert.That(()=>
    Enumerable.Range(0,1000).ToDictionary(i=>"n"+i)["n3"].ToString()
    == (3.5).ToString()
);
Run Code Online (Sandbox Code Playgroud)

输出:

PAssert.That failed for:

Enumerable.Range(0, 1000).ToDictionary(i => "n" + (object)i)["n3"].ToString() == 3.5.ToString()
             |                 |                            |         |        |        |
             |                 |                            |         |        |        "3.5"
             |                 |                            |         |        false
             |                 |                            |         "3"
             |                 |                            3
             |                 {[n0, 0], [n1, 1], [n2, 2], [n3, 3], [n4, 4], [n5, 5], [n6, 6], [n7, 7], [n8, 8], [n9, 9], ...}
             {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ...}
Run Code Online (Sandbox Code Playgroud)

对PowerAssert.NET的改进:

  • 支持静态字段和属性访问
  • 支持更多运算符,例如逻辑和按位求反。
  • 识别 C# 索引器的使用(例如dict["mykey"]==3
  • 在运算符优先级和结合性需要的地方添加括号(例如() => x - (a - b) + x * (a + b)正确重新生成)
  • 生成有效的数字和其他常量文字,包括适合表达式类型的转义符和后缀(例如1m + (decimal)Math.Sqrt(1.41)
  • 支持对象初始值设定项、对象成员初始值设定项、列表初始值设定项、扩展方法等的 C# 语法糖。
  • 使用与 Visual Studio 默认情况下相同的间距规则。
  • 支持嵌套 Lambda
  • 将泛型类型实例扩展为普通 C#;例如Func<int, bool>
  • 支持 C# 4.0 嵌入式表达式尚未使用的多种表达式树结构。

生成的项目(带有单元测试)托管在 Google 代码中,名称为ExpressionToCode - 我希望它对其他人有用。