如何在需要括号的 NUnit 中编写流畅的约束
How to write a fluent constraint in NUnit that requires parenthesis
我最近开始使用 NUnit 的约束功能和 运行 解决以下问题。我如何使用流畅的表达式语法编写约束,其中执行顺序很重要并且在正常的 C# 编程中用括号解决?
在下面的示例中,我定义了两个单独的断言:
- 字符串应以 1 或 2 开头,在所有情况下字符串应以 5 结尾
- 字符串应以 1 或 2 开头,如果字符串以 2 开头,则应以 5 结尾
要断言这一点,我可以考虑三种方式;经典的、流畅的约束和使用复合约束的约束。所以这导致了 6 个测试和一些测试用例。
private class SourceForParenthesisTest : IEnumerable
{
public IEnumerator GetEnumerator()
{
yield return new TestCaseData("2").Throws(typeof(AssertionException));
yield return new TestCaseData("3").Throws(typeof(AssertionException));
yield return new TestCaseData("15");
yield return new TestCaseData("25");
yield return new TestCaseData("35").Throws(typeof(AssertionException));
}
}
[TestCase("1", ExpectedException = typeof(AssertionException))]
[TestCaseSource(typeof(SourceForParenthesisTest))]
public void WithParenthesisClassic(string i)
{
var res = (i.StartsWith("1") || i.StartsWith("2")) && i.EndsWith("5");
Assert.True(res);
}
[TestCase("1", ExpectedException = typeof(AssertionException))]
[TestCaseSource(typeof(SourceForParenthesisTest))]
public void WithParenthesisOperatorConstraint(string i)
{
Assert.That(i, (Is.StringStarting("1") | Is.StringStarting("2")) & Is.StringEnding("5"));
}
[TestCase("1", ExpectedException = typeof(AssertionException), Ignore = true, IgnoreReason = "Not clear how to write this fluent expression")]
[TestCaseSource(typeof(SourceForParenthesisTest))]
public void WithParenthesisConstraint(string i)
{
Assert.That(i, Is.StringStarting("1").Or.StringStarting("2").And.StringEnding("5"));
}
[TestCase("1")]
[TestCaseSource(typeof(SourceForParenthesisTest))]
public void NoParenthesisClassic(string i)
{
var res = i.StartsWith("1") || i.StartsWith("2") && i.EndsWith("5");
Assert.True(res);
}
[TestCase("1")]
[TestCaseSource(typeof(SourceForParenthesisTest))]
public void NoParenthesisOperatorConstraint(string i)
{
Assert.That(i, Is.StringStarting("1") | Is.StringStarting("2") & Is.StringEnding("5"));
}
[TestCase("1")]
[TestCaseSource(typeof(SourceForParenthesisTest))]
public void NoParenthesisConstraint(string i)
{
Assert.That(i, Is.StringStarting("1").Or.StringStarting("2").And.StringEnding("5"));
}
实际问题出在 WithParenthesisConstraint(上面列出的断言 1)中,我想不出如何正确编写约束的方法,这导致我设置为忽略的一个失败测试用例。
如何编写此断言才能按预期工作?
我也看不到一个明显的方法来做到这一点。我的第一个想法是您需要将表达式解析到给定点,这使您的断言看起来像这样:
Assert.That(i, ((IResolveConstraint)Is.StringStarting("1").Or.StringStarting("2"))
.Resolve().With.StringEnding("5"))
这显然有点乱。你可以添加你自己的扩展方法,让它更愉快,使用这样的东西(你显然可以将扩展方法重命名为你认为合适的任何东西):
static class MyExtensions {
public static Constraint Evaluate(this Constraint exp) {
return ((IResolveConstraint)exp).Resolve();
}
}
这将使您的测试代码断言如下:
Assert.That(i, (Is.StringStarting("1").Or.StringStarting("2")).Evaluate()
.And.StringEnding("5"));
哪个更好一点但不理想,因此可能不是您要找的。
可能还值得考虑结合 Evaluate
和 And
约束以使其更易于阅读。也许像这样的扩展方法:
public static ConstraintExpression OnlyIf(this Constraint exp) {
return ((IResolveConstraint)exp).Resolve().And;
}
给出这样的测试代码:
Assert.That(i, (Is.StringStarting("1").Or.StringStarting("2")).OnlyIf()
.StringEnding("5"));
我最近开始使用 NUnit 的约束功能和 运行 解决以下问题。我如何使用流畅的表达式语法编写约束,其中执行顺序很重要并且在正常的 C# 编程中用括号解决?
在下面的示例中,我定义了两个单独的断言:
- 字符串应以 1 或 2 开头,在所有情况下字符串应以 5 结尾
- 字符串应以 1 或 2 开头,如果字符串以 2 开头,则应以 5 结尾
要断言这一点,我可以考虑三种方式;经典的、流畅的约束和使用复合约束的约束。所以这导致了 6 个测试和一些测试用例。
private class SourceForParenthesisTest : IEnumerable
{
public IEnumerator GetEnumerator()
{
yield return new TestCaseData("2").Throws(typeof(AssertionException));
yield return new TestCaseData("3").Throws(typeof(AssertionException));
yield return new TestCaseData("15");
yield return new TestCaseData("25");
yield return new TestCaseData("35").Throws(typeof(AssertionException));
}
}
[TestCase("1", ExpectedException = typeof(AssertionException))]
[TestCaseSource(typeof(SourceForParenthesisTest))]
public void WithParenthesisClassic(string i)
{
var res = (i.StartsWith("1") || i.StartsWith("2")) && i.EndsWith("5");
Assert.True(res);
}
[TestCase("1", ExpectedException = typeof(AssertionException))]
[TestCaseSource(typeof(SourceForParenthesisTest))]
public void WithParenthesisOperatorConstraint(string i)
{
Assert.That(i, (Is.StringStarting("1") | Is.StringStarting("2")) & Is.StringEnding("5"));
}
[TestCase("1", ExpectedException = typeof(AssertionException), Ignore = true, IgnoreReason = "Not clear how to write this fluent expression")]
[TestCaseSource(typeof(SourceForParenthesisTest))]
public void WithParenthesisConstraint(string i)
{
Assert.That(i, Is.StringStarting("1").Or.StringStarting("2").And.StringEnding("5"));
}
[TestCase("1")]
[TestCaseSource(typeof(SourceForParenthesisTest))]
public void NoParenthesisClassic(string i)
{
var res = i.StartsWith("1") || i.StartsWith("2") && i.EndsWith("5");
Assert.True(res);
}
[TestCase("1")]
[TestCaseSource(typeof(SourceForParenthesisTest))]
public void NoParenthesisOperatorConstraint(string i)
{
Assert.That(i, Is.StringStarting("1") | Is.StringStarting("2") & Is.StringEnding("5"));
}
[TestCase("1")]
[TestCaseSource(typeof(SourceForParenthesisTest))]
public void NoParenthesisConstraint(string i)
{
Assert.That(i, Is.StringStarting("1").Or.StringStarting("2").And.StringEnding("5"));
}
实际问题出在 WithParenthesisConstraint(上面列出的断言 1)中,我想不出如何正确编写约束的方法,这导致我设置为忽略的一个失败测试用例。
如何编写此断言才能按预期工作?
我也看不到一个明显的方法来做到这一点。我的第一个想法是您需要将表达式解析到给定点,这使您的断言看起来像这样:
Assert.That(i, ((IResolveConstraint)Is.StringStarting("1").Or.StringStarting("2"))
.Resolve().With.StringEnding("5"))
这显然有点乱。你可以添加你自己的扩展方法,让它更愉快,使用这样的东西(你显然可以将扩展方法重命名为你认为合适的任何东西):
static class MyExtensions {
public static Constraint Evaluate(this Constraint exp) {
return ((IResolveConstraint)exp).Resolve();
}
}
这将使您的测试代码断言如下:
Assert.That(i, (Is.StringStarting("1").Or.StringStarting("2")).Evaluate()
.And.StringEnding("5"));
哪个更好一点但不理想,因此可能不是您要找的。
可能还值得考虑结合 Evaluate
和 And
约束以使其更易于阅读。也许像这样的扩展方法:
public static ConstraintExpression OnlyIf(this Constraint exp) {
return ((IResolveConstraint)exp).Resolve().And;
}
给出这样的测试代码:
Assert.That(i, (Is.StringStarting("1").Or.StringStarting("2")).OnlyIf()
.StringEnding("5"));