NSpec - A testing framework that's like Mocha and RSpec, but for C#

NSpec is a battle hardened testing framework for C# that's heavily inspired by Mocha and RSpec. It has a fancy logo, is released under the MIT License, and is an OSS project.

Table of Contents

Getting Started

Classic .NET Framework

PM> Install-Package nspec
PM> Install-Package FluentAssertions

.NET Core

Please see Targeting .NET Core section down below.

Why NSpec?

Consistent With Modern Testing Frameworks

If you’ve used any of the following testing frameworks, you’ll feel right at home with NSpec:

Noise Free Tests

In NSpec, there is no need for access modifiers on tests, and no need to decorate test methods with attributes.

For example, this NUnit/XUnit test:

Would be written like this in NSpec (notice that there are no access modifiers or attributes):

Fluid Test Structures

You can nest a lambda within a method (you can defer inheritance hierarchies):

Here is an NSpec test that has nested structures:

Here is what you’d have to write the test above in NUnit/XUnit. It’s gross and poopy. Specifically:

Features

Lets take a look at some features of NSpec.

Assertions

NSpec has some simple assertions, but you should really just use FluentAssertions, or Shouldly, or another assertion framework. You can build your own assertions by using extension methods. For example:

Before

Want to do some setup before tests are run? Use before. The state of the class is reset with each test case (side effects/mutations don’t spill over).

Context

Test hierarchies are communicated through the context keyword. If a method contains underscores, then it will be picked up by NSpec. Any method that starts with it_ or specify_ will be treated as just a simple NUnit/XUnit style test case.

Exceptions

When your code should throw under specific conditions, you can assert that it does so with an expected Exception of known type and message.

class describe_exceptions : nspec
{
    void when_code_throws()
    {
        before = () => { };

        it["throws expected exception type"] = expect<KnownException>(() => { throw new KnownException(); });

        it["throws expected exception type and error message"] =
            expect<KnownException>("My message", () => { throw new KnownException("My message"); });
    }
}
describe exceptions
  when code throws
    throws expected exception type
    throws expected exception type and error message

2 Examples, 0 Failed, 0 Pending

Pending Tests

You can ignore tests by preceding any structure with an x. Or you can use the todo keyword provided by NSpec.

Helper Methods

Title cased (conventional C#) methods are ignored my NSpec.

Act

Here’s a fancy feature. Sometimes, what is done to a class remains the same, but the setup varies. You can use act. Each nested context will execute act before assertions are run.

Inheritance

Being able to nest tests is awesome. But you can always use inheritance to “flatten” tests if needed.

Class Level

All test structures are supported at the class level too. Here is how you’d write a before, act, and it/specify at the class level.

Debugger Support

If you want to hook into the debugger quickly, just place the following line inside of your tests. When you run NSpecRunner.exe, the debugger will pop right up:

System.Diagnostics.Debugger.Launch()

NSpec also includes DebuggerShim.cs when you install it via Nuget. So you can use TDD.NET/ReSharper to run your tests.

Console App

Or you can do something even fancier, and build your own console app! Instead of creating a Class Library for the test project, create a Console Application. Add the following code in Program.cs:

Then you can debug everything like you would any other program. More importantly, creating your own console app gives you the power to tailor input and output to your liking using NSpecs’s API/constructs.

Async/Await Support

Your NSpec tests can run asynchronous code too.

Class Level

At a class level, you still declare hook methods with same names, but they must be asynchronous and return async Task, instead of void:

For all sync test hooks at class level you can find its corresponding async one, just by turning its signature to async:

Sync Async
void before_all() async Task before_all()
void before_each() async Task before_each()
void act_each() async Task act_each()
void it_xyz() async Task it_xyz()
void specify_xyz() async Task specify_xyz()
void after_each() async Task after_each()
void after_all() async Task after_all()

Throughout the test class you can run both sync and async expectations as needed, so you can freely mix void it_xyz() and async Task it_abc().

Given a class context, for each test execution phase (before all/ before/ act/ after/ after all) you can choose to run either sync or async code according to your needs: so in the same class context you can mix e.g. void before_all() with async Task before_each(), void act_each() and async Task after_each().

What you can’t do is to assign both sync and async hooks for the same phase, in the same class context: so e.g. the following will not work and break your build at compile time (for the same rules of method overloading):

Context level

At a context and sub-context level, you need to set asynchronous test hooks provided by NSpec, instead of the synchronous ones:

For almost all sync test hooks and helpers you can find its corresponding async one:

Sync Async
beforeAll beforeAllAsync
before beforeAsync
beforeEach beforeEachAsync
act actAsync
it itAsync
xit xitAsync
expect expectAsync
todo todoAsync
after afterAsync
afterEach afterEachAsync
afterAll afterAllAsync
specify Not available
xspecify Not available
context Not needed, context remains sync
xcontext Not needed, context remains sync
describe Not needed, context remains sync
xdescribe Not needed, context remains sync

Throughout the whole test class you can run both sync and async expectations as needed, so you can freely mix it[] and itAsync[].

Given a single context, for each test execution phase (before all/ before/ act/ after/ after all) you can choose to run either sync or async code according to your needs: so in the same context you can mix e.g. beforeAll with beforeAsync, act and afterAsync.

What you can’t do is to assign both sync and async hooks for the same phase, in the same context: so e.g. the following will not work and throw an exception at runtime:

If you want to dig deeper for any level, whether class- or context-, you might directly have a look at how async support is tested in NSpec unit tests.

Just look for nspec-derived classes in following files:

Data-driven test cases

Test frameworks of the xUnit family have dedicated attributes in order to support data-driven test cases (so-called theories). NSpec, as a member of the xSpec family, does not make use of attributes and instead obtains the same result with a set of expectations automatically created through code. In detail, to set up a data-driven test case with NSpec you just:

  1. Build a set of data points.
  2. Name and assign an expectation for each data point by looping though the whole set.

Any NSpec test runner will be able to detect all the (aptly) named expectations and run them. Here you can see a sample test case, where we took advantage of NSpec.Each<> class and NSpec.Do() extension to work more easily with data point enumeration, and NSpec.With() extension to have an easier time composing text:

Additional info

Order of execution

Please have a look at this wiki page for an overview on which test hooks are executed when: execution order in xSpec family frameworks can get tricky when dealing with more complicated test configurations, like inherithing from an abstract test class or mixing before_each with before_all at different context levels.

Expect and assert

Currently with NSpec out-of-the-box you can only expect that some code under test throws, but you cannot at the same time perform any further assertion, e.g. that some flag has been set or resource disposed in a finally block.

One workaround to this scenario is to add a custom helper like the following to your test class:

class describe_expect_and_assert : nspec
{
    void ExpectAndAssert<TEx>(string testName,
        Action actionThatThrows,
        Action<TEx> assertAfter)
        where TEx : Exception
    {
        it[testName] = () =>
        {
            bool exceptionThrown = false;

            try
            {
                actionThatThrows();
            }
            catch(TEx ex)
            {
                exceptionThrown = true;
                assertAfter(ex);
            }

            if(!exceptionThrown) throw new InvalidOperationException("Exception was not thrown when expected");
        };
    }

    // ...
}

and then use it to both expect some specific Exception type to be thrown, as well as asserting some further expectation:

class describe_expect_and_assert : nspec
{
    // before_each ...

    void a_context()
    {
        // add a specification
        ExpectAndAssert<KnownException>("throws an exception",
          () => someObject.DoSomething(),
          ex => someObject.Flag.should_be(true));

        // add another specification
        ExpectAndAssert<OtherKnownException>("throws another exception",
          () => someObject.DoSomethingElse(),
          ex => someObject.OtherFlag.should_be(true));
    }
}

Targeting .NET Core

Besides targeting classic .NET Framework, NSpec supports writing tests for projects targeting .NET Core too. That means you can also run tests from console with dotnet test Command Line Interface.

.NET Core Tooling Preview 2

The following setup holds for projects based on .xproj and project.json, currently working with .NET Core 1.0 and .NET Core Tooling Preview 2.

As of today this scenario is deemed to become obsolete, once .NET Core Tooling reaches RTM with projects based back again on .csproj and MSBuild.

To setup test project you can proceed from scratch, or take advantage of xUnit template and start modifying from there. Either way, the final result is to have a project.json as the following (also targeting net451):

{
  "version": "1.0.0-*",

  "testRunner": "nspec",

  "dependencies": {
    "dotnet-test-nspec": "0.2.0",
    "LibraryUnderTest": {
      "target": "project"
    },
    "NSpec": "3.0.0",
    "Shouldly": "2.8.2"
  },

  "frameworks": {
    "netcoreapp1.0": {
      "imports": [
        "portable-net45+win8"
      ],
      "dependencies": {
        "Microsoft.Extensions.Testing.Abstractions": "1.0.0-preview2-003121",
        "Microsoft.NETCore.App": {
          "type": "platform",
          "version": "1.0.0"
        }
      }
    },
    "net451": {
    }
  }
}

From custom NSpec template

NOTE: Even if template currently creates a project supporting .NET Core Tools Preview 2 (the one based on project.json), in order to install custom CLI templates like this you need to have at least .NET Core Tools RC4 on your development machine.

From built-in XUnit template

From scratch

Whichever way you choose, project is now setup. From a command line located at test project directory, run dotnet restore. Add your test class, like the one shown at the top of this page, then from the same command line run:

> dotnet build
> dotnet test

or, from within Visual Studio 2015, build solution then open Test Explorer window in order to list and run tests.

Setup examples

See under examples/ in GitHub repository:

Extensions

NSpec in NUnit

NSpec examples can be run as NUnit tests from inside Visual Studio (using for example the ReSharper test runner) or on a CI server using the NUnit console runner. To do this, install the NSpecInNUnit package and extend a special base class. Full usage instructions are at the project site for NSpecInNUnit.

NSpec Visual Studio Adapter

NSpec.VsAdapter is a test adapter to run NSpec tests from Test Explorer in Visual Studio. It runs tests in VS 2013, 2015 and 2017 from projects targeting classic .NET Framework.

It is available both as a Visual Studio Extension as well as a NuGet Package.

Please check project home for further details and instructions.