Skip to content

MercuryNuGet/Mercury

Repository files navigation

Mercury

NuGet Version NuGet Downloads Build Status

Fluent NUnit specification extensions that will run under NCrunch, and ReSharper test runners.

#Get started

##NuGet

install-package mercury -pre

##Inherit

Inherit from MercurySuite and call Specs += for each new spec.

using Mercury;
using NUnit.Framework;

namespace MercuryExample
{
    public class MyTest : MercurySuite
    {
        protected override void Specifications()
        {
            Specs += //see below on writing specs
        }
    }
}

##Write specifications

You can copy and paste these examples directly into the array then run the tests with your favorite test runner.

###Single assert, no setup

Simplest spec is just a name and an assert:

Specs += "Simple assert".Assert(() => Assert.AreEqual(2, 1 + 1));

This will emit a single unit test with the text "Simple assert" included in the test name.

###Arrange

Next most complex is an Arrange, this is equivalent to an NUnit [SetUp] and runs for each Assert,With combination (With's are explained later):

Specs += "New List"
   .Arrange(() => new List<int>())
   .Assert("is empty", list => Assert.AreEqual(0, list.Count));

Classes under test whose constructors do not take parameters can take advantage of this shorter syntax:

.Arrange<StringBuilder>())

If you do not require a test context, you can use Arrange() with no params or types:

Specs += "No context needed because acting on static method"
    .Arrange()
    .Act(() => string.Join(",", "a", "b"))
    .Assert(joined => Assert.AreEqual("a,b", joined));

###Act

You can separate out the Act from the Assert. Here the act invokes Any() and the result is passed to the Assert.

Specs += "New List; linq says there is not any"
   .Arrange<List<int>>()
   .Act(list => list.Any())
   .Assert(any => Assert.IsFalse(any));

Or more succinctly in this case:

Specs += "New List; linq says there is not any"
   .Arrange<List<int>>()
   .Act(list => list.Any())
   .Assert(Assert.IsFalse);

###ActOn

Act returns the result of it's method. This will not work if the method is void. ActOn is also way to keep the test context. Example:

Specs += "Act version"
   .Arrange(() => new List<int>(new []{1, 2, 3}))
   .Act(list =>
   {
     list.Clear();
     return list;
   })
   .Assert(list => Assert.AreEqual(0, list.Count));

Can be just:

Specs += "ActOn version"
   .Arrange(() => new List<int>(new []{1, 2, 3}))
   .ActOn(list => list.Clear())
   .Assert(list => Assert.AreEqual(0, list.Count));

###With

With enables you to parameterise your tests. It takes between 1 and 5 generic parameters. As you can set up anoymous, you can usually get away with just one and then you also get useful names.

Specs += "When I add an item to list"
   .Arrange<List<int>>()
   .With(new {a=1})
   .ActOn((list, data) => list.Add(data.a))
   .Assert("it is exactly one long",
      (list, data) => Assert.AreEqual(1, list.Count));

With can be used in a situation without a test context using Arrange()

Specs += "Test-Context less using with"
    .Arrange()
    .With(new {a = "a", b = "b", expect = "a,b"})
    .With(new {a = "c", b = "d", expect = "c,d"})
    .Act(data => string.Join(",", data.a, data.b))
    .Assert((actual, data) => Assert.AreEqual(data.expect, actual));

With when used with mutiple data items gives more assert styles:

Specs += "Multiple data arguments in with, and three assert styles"
    .ArrangeNull()
    .With("a", "b", "a,b")
    .With("c", "d", "c,d")
    .Act((_, a, b, expected) => string.Join(",", a, b))
    .Assert((result, a, b, expected) => Assert.AreEqual(expected, result))
    .Assert((result, expected) => Assert.AreEqual(expected, result))
    .AssertEqualsExpected();

###Multiple Withs and parameter injection to test name

Use the # symbol to inject named parameters from your With data type.

Specs += "When I add #a item to list"
   .Arrange<List<int>>()
   .With(new {a=1})
   .With(new {a=2})
   .ActOn((list, data) => list.Add(data.a))
   .Assert("it is exactly one long",
      (list, data) => Assert.AreEqual(1, list.Count));

This emits two tests:

When I add 1 item to list it is exactly one long
When I add 2 item to list it is exactly one long

###Multiple Withs and Asserts

The total number of tests emitted is the number of Asserts multiplied by the number of Withs. This test below therefore runs 4 tests.

Specs += "When I add #a to list"
   .Arrange<List<int>>()
   .With(new {a=1})
   .With(new {a=2})
   .ActOn((list, data) => list.Add(data.a))
   .Assert("it is exactly one long",
      (list, data) => Assert.AreEqual(1, list.Count))
   .Assert("it contains #a",
      (list, data) => Assert.AreEqual(data.a, list[0]));

###Place expected values in the data

Where each With will generate a different expected value, include those expected values in the With data.

Specs += "When I add #expectedLength items to list"
   .Arrange<List<int>>()
   .With(new {a=new []{1,2,3},   expectedLength=3, expectedSum=6})
   .With(new {a=new []{4,6,7,9}, expectedLength=4, expectedSum=26})
   .ActOn((list, data) => list.AddRange(data.a))
   .Assert("it is exactly #expectedLength long",
      (list, data) => Assert.AreEqual(data.expectedLength, list.Count))
   .Assert("the sum is #expectedSum",
      (list, data) => Assert.AreEqual(data.expectedSum, list.Sum()));

###MercurySuite advantages

class SpecByMethodExample : MercurySuite
{
    protected override void Specifications()
    {
        Specs += "Example of spec defined in method".Assert(() => Assert.AreEqual(2, 1 + 1));

        Specs += "Lets you space out tests".Assert(() => Assert.AreEqual(2, 1 + 1));

        for (int i = 0; i < 10; i++)
        {
            Specs += "And even lets you create specs dynamically #i"
                .Arrange()
                .With(new {i})
                .Act(data => data.i*10)
                .Assert((result, data) => Assert.IsTrue(result%10 == 0));
        }
    }
}

About

Fluent NUnit test extensions

Resources

Stars

Watchers

Forks

Packages

No packages published

Languages