如何在NUnit中编写一个需要括号的流畅约束

本文关键字:一个 约束 NUnit | 更新日期: 2023-09-27 17:50:33

我最近开始使用NUnit的约束功能,遇到了以下问题。我如何使用流畅的表达式语法编写约束,其中执行顺序很重要,并且在正常的c#编程中使用括号解决?

在下面的例子中,我定义了两个独立的断言:
  1. 字符串应该以1或2开头,在任何情况下都应该以5结束
  2. 字符串应该以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),我无法想到如何正确地编写约束的方法,这导致一个失败的测试用例,我已经设置为忽略。

如何使这个断言按预期工作?

如何在NUnit中编写一个需要括号的流畅约束

我也找不到一个明显的方法来做到这一点。我的第一个想法是,您需要将表达式解析到给定的点,这使得您的断言看起来像这样:

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"));

这是更好一点,但不是理想的,所以可能不是你想要的。

还值得考虑将EvaluateAnd约束结合起来,以使其更易于阅读。可能是这样的扩展方法:

public static ConstraintExpression OnlyIf(this Constraint exp) {
    return ((IResolveConstraint)exp).Resolve().And;
}

给出这样的测试代码:

Assert.That(i, (Is.StringStarting("1").Or.StringStarting("2")).OnlyIf()
                                                              .StringEnding("5"));