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

Posted

技术标签:

【中文标题】如何在需要括号的 NUnit 中编写流畅的约束【英文标题】:How to write a fluent constraint in NUnit that requires parenthesis 【发布时间】:2015-08-04 14:29:08 【问题描述】:

我最近开始使用 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)中,我想不出一种如何正确编写约束的方法,这导致我设置为忽略的一个失败的测试用例。

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

【问题讨论】:

【参考方案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"));

这有点好,但并不理想,因此可能不是您想要的。

还值得考虑将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")); 

【讨论】:

以上是关于如何在需要括号的 NUnit 中编写流畅的约束的主要内容,如果未能解决你的问题,请参考以下文章

nUnit 和 Azure - 如何从 nUnit 启动 Dev Fabric

如何在 NUnit 中替换 Prism ContainerLocator.Container

如何迭代地运行 nunit 测试

如何使用 NUnit 3 在 Atlassian Bamboo 中运行 NUnit Runner?

如何编写稳定流畅的iOS移动端应用

你如何从 Jenkins 运行 NUnit 测试?