开发者

Is there a C# unit test framework that supports arbitrary expressions rather than a limited set of adhoc methods?

Basically NUnit, xUnit, MbUnit, MsTest and the like have methods similar to the following:

Assert.IsGreater(a,b)
//or, a lit开发者_如何学运维tle more discoverable
Assert.That(a, Is.GreaterThan(b))

However, there are a limited number of such comparison operators built-in; and they duplicate the languages operators needlessly. When I want anything even slightly complex, such as...

Assert.That(a.SequenceEquals(b))

I'm often either left digging through the manual to find the equivalent of the expression in NUnit-speak, or am forced to fall-back to plain boolean assertions with less helpful error messages.

C#, however, integrates well with arbitrary Expressions - so it should be possible to have a method with the following signature:

void That(Expression<Func<bool>> expr);

Such a method could be used to both execute the test (i.e. validate the assertion) and to also provide less-opaque diagnostics in case of test failure; after all, an expression can be rendered to pseudo-code to indicate which expression failed; and with some effort, you could even evaluate failing expressions intelligently to give some clue of the value of subexpressions.

For example:

Assert.That(()=> a == b);//could inspect expression and print a and b
Assert.That(()=> a < b && b < c);
//could mention the values of "a<b" and "b<c" and/or list the values of a, b, and c.

At a minimum, it would make the use of a parallel language for expressions unnecessary, and in some cases it might make failure messages more useful.

Does such a thing exist?

Edit: After trying (and liking!) Power Assert, I ended up reimplementing it to address several limitations. My variant of this is published as ExpressionToCode; see my answer below for a list of improvements.


Check out the PowerAssert library (example output below):

PAssert.IsTrue(() => x + 5 == d.Month * y);


System.Exception : IsTrue failed, expression was:

x + 5 == d.Month * y
| |   |  | |     | |
| |   |  | |     | 6
| |   |  | |     18
| |   |  | 3
| |   |  01/03/2010 00:00:00
| |   False
| 16
11

http://powerassert.codeplex.com/


http://satisfyr.codeplex.com/

Uses lambda expressions exactly like you described. You don't even take on a binary dependency, just add a single source file corresponding to your unit test framework.


There is actually a very good reason that NUnit provides it's own DSL rather than using normal C# expressions. It's that NUnit is supposed to work with any .NET language using the same syntax. That's not to say we can't have lambdas, just that we won't ever rely exclusively on any particular language feature.

Many of the ideas given will work and many of the third-party software solutions could be incorporated into NUnit, provided that their authors want to offer them. Of course, lots of folks prefer to keep their solutions separate and that's OK too. But talk to your favorite authors if you want them to collaborate more closely with NUnit.

In NUnit 2.5, you can use PredicateConstraint, which takes a lambda as its argument. However, the syntax is a bit limiting. The Matches keyword will work in the middle of an expression, so you can write...

Assert.That(someActual, Not.Matches( someLambda );

but doing it without the Not requires...

Assert.That(someActual, new PredicateConstraint( someLambda ));

And, of course, neither of these is as clean as the suggested syntax.

Everyone interested in this issue is welcome to join us on nunit-discuss where discussions about what should be in NUnit actually lead to action!

Charlie


Cone ( https://github.com/drunkcod/cone ) is a NUnit addin that works with 2.5.5 & 2.5.7 (other versions are only a recompile away) that gives you that capability along with a few other nifty features.


(Original Poster here)

I love PowerAssert.NET's simple syntax and messages, but the C# it produces has many issues. In particular, it doesn't support several expression features, and it doesn't add parentheses where required by operator precedence/associativity. After fixing a few bugs (and reporting them to the author) I found it'd be simpler to fix with a different approach, and to reimplement it from scratch.

The usage is similar:

PAssert.That(()=>
    Enumerable.Range(0,1000).ToDictionary(i=>"n"+i)["n3"].ToString()
    == (3.5).ToString()
);

Outputs:

PAssert.That failed for:

Enumerable.Range(0, 1000).ToDictionary(i => "n" + (object)i)["n3"].ToString() == 3.5.ToString()
             |                 |                            |         |        |        |
             |                 |                            |         |        |        "3.5"
             |                 |                            |         |        false
             |                 |                            |         "3"
             |                 |                            3
             |                 {[n0, 0], [n1, 1], [n2, 2], [n3, 3], [n4, 4], [n5, 5], [n6, 6], [n7, 7], [n8, 8], [n9, 9], ...}
             {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ...}

Improvements over PowerAssert.NET:

  • Supports static field and property access
  • Supports more operators, such as logical and bitwise negation.
  • Recognizes C# indexer use (e.g. dict["mykey"]==3)
  • Adds parentheses where required by operator precedence and associativity (e.g. () => x - (a - b) + x * (a + b) is correctly regenerated)
  • Generates valid numeric and other constant literals including escapes and suffixes as appropriate for the expression type (e.g. 1m + (decimal)Math.Sqrt(1.41))
  • Supports C# syntactic sugar for object initializers, object member initializers, list initializers, extension methods, amongst other things.
  • Uses the same spacing rules Visual Studio does by default.
  • Supports nested Lambdas
  • Expands generic type instances into normal C#; e.g. Func<int, bool>
  • Supports several expression tree constructs not yet used by C# 4.0 embedded expressions.

The resultant project (with unit tests) is hosted in google code under the name ExpressionToCode - I hope it's useful to others.


No one that I know of, but I think this can be added to the upcoming NUnit 3 wishlist.

Some work to that end done in #TestEx http://sharptestex.codeplex.com/ is being thought for inclusion, but you can add a blueprint/issue for the more general approach you are asking.

https://blueprints.launchpad.net/nunit-3.0

https://bugs.launchpad.net/nunit-3.0


The Visual Studio 2010 unit test framework has a CollectionAssert class which is usefull.

It also provides an Assert.IsTrue(bool) for generic cases that you craft yourself but none that use expressions;

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜