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

时间:2015-05-22 14:05:20

标签: c# unit-testing nunit fluent-interface

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

在下面的示例中,我定义了两个单独的断言:

  1. 字符串应以1或2开头,并且在所有情况下字符串应以5
  2. 结尾
  3. 字符串应以1或2开头,如果字符串以2开头,则应以5结尾
  4. 断言这一点,我可以考虑三种方式;使用复合约束的经典,流畅约束和约束。因此,这会产生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如上所列),我无法想出如何正确编写约束的方法,这导致我设置为忽略的一个失败测试用例。

    如何编写此断言以按预期工作?

1 个答案:

答案 0 :(得分:2)

我也看不到明显的方法。我的第一个想法是你需要将表达式解析到给定的点,这样你的断言就像这样:

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