Project Description

NJamb is a C# syntax for tests and DDD specifications. It makes them more readable, faster to write, and more rigorous. Its Linq-style expressions can assert preconditions and postconditions. IntelliSense makes the syntax almost foolproof. And, it's designed to be extended.

Sample NUnit fixture

The classic pattern for unit tests is arrange-act-assert, and the more recent BDD pattern of given-when-then isn't much different.  This is fine as far as it goes, but it really only measures outcomes; it doesn't guard against garbage-in-garbage-out.  If the SUT (system under test) happens to be in a state that would pass the test before the "act" or "when",  the test gives a false pass. 

An NJamb tests asserts immediately before a change, performs the change, and only then measures outcomes.  Here's what it looks like in practice: 

[TestFixture]
public class SchoolbusFixture : ExperimentOn<Schoolbus>
{
[Test]
public void CanTransportKids()
{
Expecting(ThatTheCollection(x => x.Tires).Has.CountOf(6).Always(),
ThatTheCollection(x => x.Tires).Has.No.ItemsSatisfying(y => y.Punctured) //
.AndLater.Has.AtLeast(2).ItemsSatisfying(y => y.Punctured),
That(x => x.CanTransportKids()).WillChangeTo(false));

Try(x => x.Tires.First().Punctured = true);
}

protected override Schoolbus TestSubjectFactory()
{
return new Schoolbus();
}
}

The Expecting() method takes IDeltaSpecification<T> arguments. Each of these is a pair of assertions: one about the SUT immediately before a change, and one about the SUT immediately after. By examining the SUT both before and after the change, these assertions are much more likely to describe the true inputs and outpus of the action. 

There are several other benefits to asserting expectations between "arrange" and "act":

  1. It is more explicit documentation of what the author of the test expects.
  2. Because it forces the developer to be explicit, it forces the developer to think about design.
  3. It can change how you write tests in much the same way that test-first changes how you write production code: when you write a delta assertion before any other part of the test, you can use its preconditions to drive the setup of the test. It's a more thoughtful, more focused flow.
  4. Preconditions provide a degree of safety when refactoring tests, especially those with shared setups.  Again, like production code.

Design Principles

  • NJamb is readable.  It makes extensive use of generics, but its syntax is deliberately structured so that the compiler can infer types as you go; you don't have to type angle brackets.
  • NJamb is fast to write. Much of its syntax is type-constrained so that its terms are only available when they make.  For example,
Specify.For(() => string.Empty).That(x => x.GetHashCode()).Is.GreaterThan(0)

is valid syntax because GetHashCode returns an int, which implements IComparable<int>.  Thus, string comparisons are only offered to strings; testing for NaN is only offered to float and double; Is.ReadOnly is available for an ICollection<T> but not for an IEnumerable<T>; etc. Modern VS tools such as ReSharper apply this knowledge as you type.  It's better than compile-time checking--it's compose-time checked.

  • NJamb specifications are self-describing: their failure messages are highly readable.  For example, when the above test fails (correctly) because only one tire has been punctured and two were expected, the output is:
expected TestSubject.Tires would have at least 2 items satisfying y => y.Punctured
but had 1 such item.

There are several aspects of self-describing specifications worth noting:

  1. NJamb (mostly) eliminates the need for hand-crafted error messages.  You don't have to litter your tests with string literals (hence the name?) that are stale or nonsensical after the first refactor.
  2. NJamb failure strings are lazy: they aren't constructed until the specification has been evaluated and found to fail.
  3. NJamb is smart about printing.  The framework's .ToString() of a variable like
var list = new List<int>{3, 1, 2};
is ugly and barely informative:
System.Collections.Generic.List`1[System.Int32]
NJamb prints that variable as
List<int> {3, 1, 2}
  • NJamb is extensible. Much of its built-in syntax relies on extension methods: this is how the type-appropriate syntax works, together with generic type parameter constraints. Because the entire DSL is designed with extension methods in mind, it's full of hooks for your extension methods. They plug right in. You won't even have to recompile the NJamb binaries.
  • NJamb is strongly (staticly) typed. For example, in the fixture above, the argument to
.WillChangeTo(...)

is constrained to be a bool because the expression

x => x.CanTransportKids()

returns a bool.  This is much different than classic NUnit assertion syntax, much of which dates back to the era before .NET generics and to its Java predecessor.

  • NJamb will work in your test framework. Although it's developed with NUnit and includes helper assemblies out of the box, none of the core of NJamb depends on NUnit. NJamb can be used in any test framework that allows you to assert a failure with a string explanation.

Roadmap in brief

Version 0.x and eventually v1 of NJamb is aimed at tests.  However, there are at least two other areas of the .NET framework that could benefit from its fluent syntax and self-describing specifications and predicates: DDD-style specifications, and Design by Contract.  Those are planned.

Version 0.1 is being developed in .NET 4.0. Initial builds will target "Any CPU" and not Silverlight.  There's not much in NJamb itself that should have problems in SL or .NET 3.x, but some of the reflection helpers, etc., in Stile might not map to SL easily.  As of this writing in Feb 2011, the project is a typical open-source initial release: reasonably feature-complete but under-documented, and seeking an audience.  Packaging will depend on demand.

Why's it called NJamb?

It starts with an N because it is of and from .NET.  It puns on "enjamb", which is a term of art in poetry, used when a line breaks in the middle of a sentence, or even a word. According to Wikipedia, "The term is directly borrowed from the French enjambement, meaning 'straddling' or 'bestriding'." Straddling is pretty much what the testing side of NJamb is all about: putting an assertion on both sides of a change.

But the main reason is coincidence. Only days before the author of NJamb was going to have to put a name to this project, he drove past a hand-lettered sign outside a heating & airconditioning dealer.  He misread the sign because it was in all-caps and lacked any punctuation.  He did a huge double-take and almost missed a stop sign.  He had mentally punctuated it this way:

ENJOY A NEW FIREPLACE!
INSERT THIS, WINTER.

It was mostly the enjambment that threw him. This put the word in his mind and, embarassingly, continues to please him. 

What's all that Stile stuff?

That's another project that will probably stand alone soon.  It's a collection of helpers, base classes, and implemented patterns.  Much of it tries to make the C# system libraries more readable.

For instance, given an IList<T> that you want to make readonly, instead of writing

new ReadOnlyCollection<T>(list)

there's an extension method in Stile that's more terse and more readable:

list.ToReadOnly()

Why's that one called Stile?

More punning, I'm afraid. A stile is a sort of bridge over a fence, or an opening in it, design to let people through while keeping livestock in. (The more familiar term turnstile is a particular variation.) Also, it rhymes with style. The Stile library tries to make it easier for people who know what they're doing to get where they need to go, and maybe even write more expressive code along the way.

Last edited May 28, 2011 at 9:16 PM by MarkKnell, version 24