vendredi 22 mai 2015

How to write a fluent constraint in NUnit that requires parenthesis

I recently started working with the Constraint functionality of NUnit and ran into the following question. How can I write a constraint using the fluent expression syntax where the order of execution is important and in normal C# programming is solved with parenthesis?

In the following example I define two separate assertions:

  1. A string should start with 1 or 2 and in all cases the string should end with 5
  2. A string should start with 1 or 2, and in the cases where the string starts with 2 it should end with 5

To assert this, I can think about three ways; classic, fluent constraints and constraints using compound constraints. So this results in 6 tests and some test cases.

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

The actual problem is in WithParenthesisConstraint (assert 1 as listed above), I couldn't think about a way how to write the constraint correctly and this results in one failing test case that I have set to ignored.

How do I write this assert to work as expected?

Aucun commentaire:

Enregistrer un commentaire