Fork me on GitHub

Getting Started

Write Your First Spec

This tutorial will walk you through installing NSpec and running a simple spec.

  1. Open Visual Studio.
  2. Ensure that you have NuGet installed. (NuGet only works with VS2010+. To use NSpec with VS2008, you must download the binaries zip.)
  3. Create a .Net 4.0 or .Net 3.5 class library project.
  4. Open the Package Manager Console and type the following command:

    PM> Install-Package nspec

  5. After installation completes, create a class with the following code:
using NSpec;

class my_first_spec : nspec
{
    void before_each() { name = "NSpec"; }

    void it_works()
    {
        name.should_be("NSpec");
    }

    void describe_nesting()
    {
        before = () => name += " BDD";

        it["works here"] = () => name.should_be("NSpec BDD");

        it["and here"] = () => name.should_be("NSpec BDD");
    }
    string name;
}
    

Run Your First Spec

Nuget placed NSpecRunner.exe in your PATH on the Package Manager Console.

  1. Build the class library.
  2. Run the following command in the Package Manager Console:
 NSpecRunner.exe YourClassLibraryName\bin\debug\YourClassLibraryName.dll
my first spec
  it works
  describe nesting
    works here
    and here

4 Examples, 0 Failed, 0 Pending
    

Debugger Support

DebuggerShim.cs is a class file included with NSpec, you can use that to debug a test using TestDriven.net or Resharper's test runner (we're working on fully integrated support).

Definitely try specwatchr. Specwatchr will automatically run impacted tests for you when you save a file. If you ever need to debug a test simply use this line of code in your test:

    [...]

    System.Diagnostics.Debugger.Launch();

    [...]
    

Mono Support

Mono binaries and samples are available here. Simply clone/download this repository.

Why NSpec? What NSpec Brings to the Table.

Consistent with other BDD testing frameworks

If you've used any of the following testing frameworks, you'll feel right at home with NSpec:
  • RSpec
  • Minitest
  • Jasmine
  • Mocha

Less Ceremony (save your keystrokes)

  • No need for access modifiers on tests.
  • NSpec test can live side by side with test from other testing frameworks (in the same project).
  • No need to attribute test.
  • Easy transition from xUnit, NUnit, and MSTest frameworks.

NUnit, XUnit, MSTest force you to place attributes and public access modifiers in your test suites:

[TestFixture]
public class describe_NUnit
{
    [Test]
    public void it_just_works()
    {
        Assert.AreEqual("hello", "hello"); //this is an arbitrary assertion
    }
}
    

NSpec uses underscore conventions, making your tests cleaner:

class describe_NSpec : nspec
{
    void it_just_works()
    {
        "hello".should_be("hello"); //this is an arbitrary assertion
    }
}
    

Fluid Test Structures

  • Elaborating a context can be done at the method level.
  • Reduce the ceremony of inheritance when it isn't needed.

NUnit, XUnit, MSTest force inheritance if you want to build on a context:

[TestFixture]
public class describe_NUnit
{
    [SetUp]
    public void before_each()
    {
        Console.WriteLine("I run before each test.");
    }

    [Test]
    public void it_works_here()
    {
        Assert.AreEqual("hello", "hello");
    }
}

[TestFixture]
public class category_of_examples : describe_NUnit
{
    [SetUp]
    public void before_each_for_this_context()
    {
        Console.WriteLine("I run before each test defined in this context.");
    }

    [Test]
    public void it_also_works_here()
    {
        Assert.AreEqual("hello", "hello");
    }

    [Test]
    public void it_works_here_too()
    {
        Assert.AreEqual("hello", "hello");
    }
}
    

NSpec can do that too (with less code):

class describe_NSpec : nspec
{
    void before_each()
    {
        Console.WriteLine("I run before each test.");
    }

    void it_works_here()
    {
        "hello".should_be("hello");
    }
}

class category_of_examples : describe_NSpec
{
    void before_each()
    {
        Console.WriteLine("I run before each test defined in this context.");
    }

    void it_also_works_here()
    {
        "hello".should_be("hello");
    }

    void it_works_here_too()
    {
        "hello".should_be("hello");
    }
}
    

but NSpec also provides a more concise option:

class describe_NSpec : nspec
{
    void before_each()
    {
        Console.WriteLine("I run before each test.");
    }

    void it_works_here()
    {
        "hello".should_be("hello");
    }

    void a_category_of_examples()
    {
        before = () => Console.WriteLine("I run before each test defined in this context.");

        it["also works here"] = () => "hello".should_be("hello");

        it["works here too"] = () => "hello".should_be("hello");
    }
}
    

specifications

using NSpec;

class describe_specifications : nspec
{
    void when_creating_specifications()
    {
        //some of these specifications are meant to fail so you can see what the output looks like
        it["true should be false"] = () => true.should_be_false();
        it["enumerable should be empty"] = () => new int[] { }.should_be_empty();
        it["enumerable should contain 1"] = () => new[] { 1 }.should_contain(1);
        it["enumerable should not contain 1"] = () => new[] { 1 }.should_not_contain(1);
        it["1 should be 2"] = () => 1.should_be(2);
        it["1 should be 1"] = () => 1.should_be(1);
        it["1 should not be 1"] = () => 1.should_not_be(1);
        it["1 should not be 2"] = () => 1.should_not_be(2);
        it["\"\" should not be null"] = () => "".should_not_be_null();
        it["some object should not be null"] = () => someObject.should_not_be_null();
        //EXPERIMENTAL - specify only takes a lambda and does 
        //its best to make a sentence out of the code. YMMV.
        specify = ()=> "ninja".should_not_be("pirate");
    }
    object someObject = null;
}
describe specifications
  when creating specifications
    true should be false - FAILED - Expected: False, But was: True
    enumerable should be empty
    enumerable should contain 1
    enumerable should not contain 1 - FAILED - Expected: not collection containing 1, But was:...
    1 should be 2 - FAILED - Expected: 2, But was: 1
    1 should be 1
    1 should not be 1 - FAILED - Expected: not 1, But was: 1
    1 should not be 2
    "" should not be null
    some object should not be null - FAILED - Expected: not null, But was: null
    ninja should not be pirate

**** FAILURES ****

nspec. describe specifications. when creating specifications. true should be false.
Expected: False, But was: True
   at describe_specifications.<when_creating_specifications>b__0() in c:\Development\NSpec\src...

nspec. describe specifications. when creating specifications. enumerable should not contain 1.
Expected: not collection containing 1, But was: < 1 >
   at describe_specifications.<when_creating_specifications>b__3() in c:\Development\NSpec\src...

nspec. describe specifications. when creating specifications. 1 should be 2.
Expected: 2, But was: 1
   at describe_specifications.<when_creating_specifications>b__4() in c:\Development\NSpec\src...

nspec. describe specifications. when creating specifications. 1 should not be 1.
Expected: not 1, But was: 1
   at describe_specifications.<when_creating_specifications>b__6() in c:\Development\NSpec\src...

nspec. describe specifications. when creating specifications. some object should not be null.
Expected: not null, But was: null
   at describe_specifications.<when_creating_specifications>b__9() in c:\Development\NSpec\src...

11 Examples, 5 Failed, 0 Pending

custom assertions

If you don't like the assertions provided by NSpec. You can create your own. Here is an example:
class custom_assertions : nspec
{
    void it_is_a_custom_assertion()
    {
        16.is_less_than(30);
    }
}

public static class AssertionExtensions
{
    public static void is_less_than(this int left, int right)
    {
         if(left >= right)
         {
             throw new InvalidOperationException(
                string.Format("{0} was not less than {1}", left, right));
         }
    }
}

  

NSpec also plays nice with assertion packages such as Fluent.Assertions.

before

using NSpec;

class describe_before : nspec
{
    void they_run_before_each_example()
    {
        before = () => number = 1;
        it["number should be 2"] = () => (number = number + 1).should_be(2);
        //even though it was incremented in the previous example
        //the before runs again for each spec resetting it to 1
        it["number should be 1"] = () => number.should_be(1);
    }
    int number;
}
describe before
  they run before each example
    number should be 2
    number should be 1

2 Examples, 0 Failed, 0 Pending

contexts

using NSpec;

class describe_contexts : nspec
{
    //context methods require an underscore. For more info see DefaultConventions.cs.
    void describe_Account()
    {
        //contexts can be nested n-deep and contain befores and specifications
        context["when withdrawing cash"] = () =>
        {
            before = () => account = new Account();
            context["account is in credit"] = () =>
            {
                before = () => account.Balance = 500;
                it["the Account dispenses cash"] = () => account.CanWithdraw(60).should_be_true();
            };
            context["account is overdrawn"] = () =>
            {
                before = () => account.Balance = -500;
                it["the Account does not dispense cash"] = () => account.CanWithdraw(60).should_be_false();
            };
        };
    }
    private Account account;
}
describe contexts
  describe Account
    when withdrawing cash
      account is in credit
        the Account dispenses cash
      account is overdrawn
        the Account does not dispense cash

2 Examples, 0 Failed, 0 Pending

pending

using NSpec;

class describe_pending : nspec
{
    void when_creating_pending_specifications()
    {
        it["pending spec"] = todo;
        //or just add an 'x' at the beginning of a specification that isn't quite ready
        xit["\"\" should be \"something else\""] = () => "".should_be("something else");
    }
}
describe pending
  when creating pending specifications
    pending spec - PENDING
    "" should be "something else" - PENDING

2 Examples, 0 Failed, 2 Pending

helpers

using NSpec;

class describe_helpers : nspec
{
    void when_making_tea()
    {
        context["that is 210 degrees"] = () =>
        {
            before = () => MakeTea(210);
            it["should be hot"] = () => tea.Taste().should_be("hot");
        };
        context["that is 90 degrees"] = () =>
        {
            before = () => MakeTea(90);
            it["should be cold"] = () => tea.Taste().should_be("cold");
        };
    }
    //helper methods do not have underscores
    void MakeTea(int temperature)
    {
        tea = new Tea(temperature);
    }
    Tea tea;
}
describe helpers
  when making tea
    that is 210 degrees
      should be hot
    that is 90 degrees
      should be cold

2 Examples, 0 Failed, 0 Pending

act

using NSpec;

[Tag("describe_act")]
class describe_batman_sound_effects_as_text : nspec
{
    void they_are_loud_and_emphatic()
    {
        //act runs after all the befores, and before each spec
        //declares a common act (arrange, act, assert) for all subcontexts
        act = () => sound = sound.ToUpper() + "!!!";
        context["given bam"] = () =>
        {
            before = () => sound = "bam";
            it["should be BAM!!!"] = 
                () => sound.should_be("BAM!!!");
        };
        context["given whack"] = () =>
        {
            before = () => sound = "whack";
            it["should be WHACK!!!"] = 
                () => sound.should_be("WHACK!!!");
        };
    }
    string sound;
}
describe batman sound effects as text
  they are loud and emphatic
    given bam
      should be BAM!!!
    given whack
      should be WHACK!!!

2 Examples, 0 Failed, 0 Pending

class level

using NSpec;

class describe_class_level : nspec
{
    //before, act, and it can also be declared at the class level like so
    void before_each()
    {
        sequence = "arrange, ";
    }
    void act_each()
    {
        sequence += "act";
    }

    //prefixing a method with "it_" or "specify_"
    //will tell nspec to treat the method as an example
    void specify_given_befores_and_acts_run_in_the_correct_sequence()
    {
        sequence.should_be("arrange, act");
    }

    string sequence;
}
describe class level
  specify given befores and acts run in the correct sequence

1 Examples, 0 Failed, 0 Pending

inheritance

using NSpec;

[Tag("describe_inheritance")]
class given_the_sequence_continues_with_2 : given_the_sequence_starts_with_1
{
    void before_each()
    {
        sequence += "2";
    }
    void given_the_sequence_continues_with_3()
    {
        before = () => sequence += "3";

        //the befores run in the order you would expect
        it["sequence should be \"123\""] =
            () => sequence.should_be("123");
    }
}

class given_the_sequence_starts_with_1 : nspec
{
    void before_each()
    {
        sequence = "1";
    }
    protected string sequence;
}
given the sequence starts with 1
  given the sequence continues with 2
    given the sequence continues with 3
      sequence should be "123"

1 Examples, 0 Failed, 0 Pending

exception

using System;
using NSpec;

class describe_exception : nspec
{
    void given_a_null_string()
    {
        it["should throw null-ref"] =
            expect<NullReferenceException>(() => nullString.Trim());
    }
    string nullString = null;
}
describe exception
  given a null string
    should throw null-ref

1 Examples, 0 Failed, 0 Pending

Summary

  • Install via NuGet (VS2010+)
  • or download zip from github https://github.com/mattflo/NSpec/downloads
  • Requires .NET 3.5
  • Classes inherit from nspec
  • Classes and methods
    • are contexts
    • do not require an access modifier
  • Inside methods
    • sub contexts are created (context/describe)
    • specifications are declared (it/specify)
    • before lambdas are assigned that run before each specification
    • act lambdas are assigned that run after befores but before specifications