Try fast search NHibernate

25 March 2009

NUnitEx : assertion.Should().Be.Fluent();

Continuing the implementation from the previous post, NUnitEx look nice and useful.

I tried to check available features “simulating” the use of NUnitEx assertions inside some frameworks where I am involved.

Before show some new examples, I would thank to Davy, Steve and Gustavo for their help in the API definition (by the way if you find something wrong is only my fault).

New constrain chain

string something = "something";

something.Should()
.StartWith("so")
.And
.EndWith("ing")
.And
.Contain("meth");

something.Should()
.StartWith("so")
.And
.EndWith("ing")
.And
.Not.Contain("body");
var ints = new[] { 1, 2, 3 };
ints.Should()
.Have.SameSequenceAs(new[] { 1, 2, 3 })
.And
.Not.Have.SameSequenceAs(new[] { 3, 2, 1 })
.And
.Not.Be.Null()
.And
.Not.Be.Empty();

New ValueOf

ValueOf is a new property available in certain cases when we know the type during “constraints-chain”.

var instanceOfClass = new D2();

instanceOfClass.Should()
.Be.AssignableTo<D1>()
.And.Be.OfType<D2>()
.And.ValueOf.StringProperty
.Should().Be.Null();

New Actions constrains

(new Action(() => new AClass(null)))
.Should().Throw<ArgumentNullException>()
.And.
ValueOf.ParamName
.Should().Be.Equal("obj");

Obviously you can mix the classic assertion with NUnitEx API:

Assert.Throws<ArgumentNullException>(() => new AClass(null))
.ParamName.Should().Be.Equal("obj");

Exceptions assertions

These are some examples using NUnitEx syntax + LINQ

Giving:

public class SillyClass
{
public SillyClass(object obj)
{
if (obj == null)
{
throw new ArgumentException("mess",
new ArgumentNullException("mess null",
new ArgumentOutOfRangeException("obj")));
}
}
}

possible assertions are:

new Action(() => new SillyClass(null))
.Should().Throw<ArgumentException>()
.And.ValueOf.InnerExceptions()
.OfType<ArgumentOutOfRangeException>().First()
.ParamName.Should().Be.Equal("obj");

new Action(() => new SillyClass(null))
.Should().Throw<ArgumentException>()
.And.ValueOf.InnerExceptions().Select(e => e.GetType())
.Should().Contain(typeof(ArgumentOutOfRangeException));

new Action(() => new SillyClass(null))
.Should().Throw<ArgumentException>()
.And.ValueOf.InnerException
.Should().Be.OfType<ArgumentNullException>()
.And.ValueOf.Message.Should().Be.Equal("mess null");

What next

I didn’t see so many interest to have API available for NUnit2.4.8 so I would jump this step.

In term of NUnitEx internals what will be interesting is its implementation for xUnit and MbUnit, if somebody have interest I’m happy to share the project (somebody named Lars for example ;-) ).


kick it on DotNetKicks.com

12 comments:

  1. Looks great. I will download it and start testing it...
    Can you change StartsWith to StartWith and EndsWith to EndWith? Thats the way it should be with Should (like Contain)
    It will be difficult to do it after people start to adopt it.

    ReplyDelete
  2. Ah, and do you have an user group for NUnitEx? You know, i feel i don't have enough places to read and discuss (lol)

    ReplyDelete
  3. Fixed thanks.

    So far I'm using the NUnit google-group.

    ReplyDelete
  4. IMO, it's getting down-right ridiculous and NOT readable. I mean, the point is to have readable asserts, right?

    ReplyDelete
  5. Hi Fabio,

    I would certainly like to join you on the project. But I have to agree with cmartin on some stuff.


    var ints = new[] { 1, 2, 3 };
    ints.Should()
    .Have.SameSequenceAs(new[] { 1, 2, 3 })
    .And
    .Not.Have.SameSequenceAs(new[] { 3, 2, 1 })
    .And
    .Not.Be.Null()
    .And
    .Not.Be.Empty();

    Is not readable. I don't think the major concern should be "good" english, but readable code. Another good reason for me using this fluent syntax, is, that when it comes to my mind "i should assert this value to be..." i can write it straight away without having to go back. This leads to more assertions which might or might not be a good thing :-)

    I guess, I would not use this syntax. Just compare:

    var ints = new[] { 1, 2, 3 };
    ints.ShouldNotBeNullOrEmpty()
    .ShouldContainInOrder(1, 2, 3)
    .ShouldNotContainInOrder(3, 2, 1)

    All the points you use just make it messy IMO.

    Compare again:

    new Action(() => new SillyClass(null))
    .Should().Throw<ArgumentException>()
    .And.ValueOf.InnerExceptions()
    .OfType<ArgumentOutOfRangeException>().First()
    .ParamName.Should().Be.Equal("obj");

    Or:

    var exc = new Action(() => new SillyClass(null))
    .ShouldThrow<ArgumentException>();

    exc.InnerExceptions()
    .First(e => e is ArgumentOutOfRangeException)
    .As<ArgumentOutOfRangeException>
    .ParamName.ShouldBeEqual("obj")

    Feeling this project is going into the wrong direction - Allthoug I would love to have a fluent syntax to assertions.

    One thing I thought of alot in the last time is to have a common assertion lib that is not only limited to life-time assertions.

    Couldn't we use Contracts.NET, wrap it into a fluent API and make all the tests recognize it as failed assertions. Maybe the set of methods for tests would be a little bit extended. But it would be great to use some of it in production, too:

    MyObject.ShouldNotBeNull(o => o.Property).....

    The reason for using lambda syntax is, too be able to throw a exception containing the property name. That might be interesting for UnitTest-Assertions as well.

    Ten un buen dia! Y gracias por todo el trabajo que haces por la "community"!

    ReplyDelete
  6. @cmartin
    Interesting and very constructive critic.

    ReplyDelete
  7. @Lars
    You know I begun from a different point. After feedback I have changed the API and I tried it.
    Really it work very well inside the IDE.

    About the assertion on exception:
    You can use the mix-syntax (NUnit-Classic+NUnitEx)
    Assert.Throws<ArgumentException>(() => new SillyClass(null)) .InnerExceptions().OfType<ArgumentOutOfRangeException>().First() .ParamName.Should().Be.Equal("obj");

    About lamda:
    MyObject.ShouldNotBeNull(o => o.Property) for what ?
    To have the name of the property in the message ?

    ReplyDelete
  8. > You know I begun from a different point.
    You had the Syntax without points before, or what?

    > About lambda
    Right. But this is more interesting for production assertions then for unit tests.

    ReplyDelete
  9. Public without "so many point".
    My first local implementation was without dots but the problem, without dots, is that you must remember the syntax very well because you must write the entirely assertion. Using dots the intellisense is "your friend", in each dot, it is saying you which is the next possible step and dots are useful even to as separator of each word.

    ReplyDelete
  10. What about the readability? Do you agree on that it kind of suchs, or do you find it quite readable?

    ReplyDelete
  11. The most readable is natural language
    In Italian
    {1, 2, 3} dovrebbe avere la stessa sequenza di {1, 2, 3}

    In Spanish
    {1, 2, 3} tendría que tener la misma secuencia de {1, 2, 3}

    In English
    {1, 2, 3) should have same sequence as {1, 2, 3}

    In C#
    (new[] { 1, 2, 3 }).Should.Have.SameSequenceAs(new[] { 1, 2, 3 })

    ReplyDelete
  12. In Italian
    {1, 2, 3} dovrebbe avere la stessa sequenza di {1, 2, 3} ed essere un sub-insieme di {5,1,2,3}

    In Spanish
    {1, 2, 3} tendría que tener la misma secuencia de {1, 2, 3} y ser un subconjunto de {5,1,2,3}

    In English
    {1, 2, 3) should have same sequence as {1, 2, 3} and be a subset of {5,1,2,3}

    In C#
    (new[] {1,2,3}).Should().Have.SameSequenceAs(new[] {1,2,3}).And.Be.SubsetOf(new[] {5,1,2,3})

    ReplyDelete