24

I have been using MSpec to write my unit tests and really prefer the BDD style, I think it's a lot more readable. I'm now using Silverlight which MSpec doesn't support so I'm having to use MSTest but would still like to maintain a BDD style so am trying to work out a way to do this.

Just to explain what I'm trying to acheive, here's how I'd write an MSpec test

[Subject(typeof(Calculator))]    
public class when_I_add_two_numbers : with_calculator
{
  Establish context = () => this.Calculator = new Calculator();
  Because I_add_2_and_4 = () => this.Calculator.Add(2).Add(4);
  It should_display_6 = () => this.Calculator.Result.ShouldEqual(6);
}

public class with_calculator
{
  protected static Calculator;
}

So with MSTest I would try to write the test like this (although you can see it won't work because I've put in 2 TestInitialize attributes, but you get what I'm trying to do..)

[TestClass]
public class when_I_add_two_numbers : with_calculator
{
   [TestInitialize]
   public void GivenIHaveACalculator()
   {
      this.Calculator = new Calculator();
   }

   [TestInitialize]
   public void WhenIAdd2And4()
   {
      this.Calculator.Add(2).Add(4);
   }

   [TestMethod]
   public void ThenItShouldDisplay6()
   {
      this.Calculator.Result.ShouldEqual(6);
   }
}

public class with_calculator
{
  protected Calculator Calculator {get;set;}
}

Can anyone come up with some more elegant suggestions to write tests in this way with MSTest?

John Zabroski
  • 2,212
  • 2
  • 28
  • 54
Charlie
  • 10,227
  • 10
  • 51
  • 92
  • Maybe I missed it, but why use MSTest (an xUnit tool) when you clearly want MSpec (a Spec family tool)? You *can* make MSTest behave kinda-sorta like MSpec, however it would be a kludge. e.g. How would you generate readable specs from the MSpec-style xUnit tests ? – Gishu Aug 03 '12 at 06:41
  • I am a huge fan of MSpec (and Machine.Fakes) but back when I asked this question I was on a project where I was told to use MSTest (I would never opt for that myself!). This approach outline in the answer below worked quite well given the tooling constraints. You'll be pleased to hear I have since moved on and am once again happily MSpecing! – Charlie Aug 03 '12 at 14:32
  • Quite pleased! Good for you. My peeve with such solutions is that it is harder for maintainers from both camps to understand such compromises. – Gishu Aug 04 '12 at 09:17
  • Several years later, I'd strongly suggest to try [NSpec](https://github.com/mattflo/NSpec) (Disclaimer: I recently contributed to it) – superjos Nov 30 '15 at 17:10

5 Answers5

35

What you think about this one:

[TestClass]
public class when_i_add_two_numbers : with_calculator
{
    public override void When()
    {
        this.calc.Add(2, 4);
    }

    [TestMethod]
    public void ThenItShouldDisplay6()
    {
        Assert.AreEqual(6, this.calc.Result);
    }

    [TestMethod]
    public void ThenTheCalculatorShouldNotBeNull()
    {
        Assert.IsNotNull(this.calc);
    }
}

public abstract class with_calculator : SpecificationContext
{
    protected Calculator calc;

    public override void Given()
    {
        this.calc = new Calculator();
    }
}

public abstract class SpecificationContext
{
    [TestInitialize]
    public void Init()
    {
        this.Given();
        this.When();
    }

    public virtual void Given(){}
    public virtual void When(){}
}

public class Calculator
{
    public int Result { get; private set; }
    public void Add(int p, int p_2)
    {
        this.Result = p + p_2;
    }
}
Charlie
  • 10,227
  • 10
  • 51
  • 92
goenning
  • 6,514
  • 1
  • 35
  • 42
  • If we were to add another SpecificationContext, `with_different_calculator` and you also wanted to test `when_i_add_two_numbers` on that context, what would you suggest calling that TestClass? – ajbeaven Jun 04 '13 at 06:25
  • How about removing the `Given` and `When` and just use the constructors in `with_calculator` and `when_i_add_two_numbers` classes? – Pellared Mar 20 '14 at 10:40
2

Mark Nijhof has an example of doing Given-When-Then style testing with NUnit in his Fohjin.DDD github repository.

Here's an excerpt from the example referenced above:

public class When_registering_an_domain_event : BaseTestFixture<PreProcessor>
{
    /* ... */

    protected override void When()
    {
        SubjectUnderTest.RegisterForPreProcessing<ClientMovedEvent>();
        SubjectUnderTest.Process();
    }

    [Then]
    public void Then_the_event_processors_for_client_moved_event_will_be_registered()
    {
        IEnumerable<EventProcessor> eventProcessors;
        EventProcessorCache.TryGetEventProcessorsFor(typeof(ClientMovedEvent), out eventProcessors);
        eventProcessors.Count().WillBe(1);
    }
}

And you can see the Given in the base class implementation:

[Given]
public void Setup()
{
    CaughtException = new NoExceptionWasThrownException();
    Given();

    try
    {
        When();
    }
    catch (Exception exception)
    {
        CaughtException = exception;
    }
    finally
    {
        Finally();
    }
}
Community
  • 1
  • 1
Kaleb Pederson
  • 45,767
  • 19
  • 102
  • 147
1

I've been giving this sort of question a lot of though recently. There are a lot of reasonable options out there, and you can create your own easily, as displayed in some of the answers in this post. I've been working on a BDD testing framework with the intent being to make it easily extended to any unit testing framework. I currently support MSTest and NUnit. Its called Given, and it's opensource. The basic idea is pretty simple, Given provides wrappers for common sets of functionality which can then be implemented for each test runner.

The following is an example of an NUnit Given test:

[Story(AsA = "car manufacturer",
       IWant = "a factory that makes the right cars",
       SoThat = "I can make money")]
public class when_building_a_toyota : Specification
{
    static CarFactory _factory;
    static Car _car;

    given a_car_factory = () =>
                              {
                                  _factory = new CarFactory();
                              };

    when building_a_toyota = () => _car = _factory.Make(CarType.Toyota);

    [then]
    public void it_should_create_a_car()
    {
        _car.ShouldNotBeNull();
    }

    [then]
    public void it_should_be_the_right_type_of_car()
    {
        _car.Type.ShouldEqual(CarType.Toyota);
    }
}

I tried my best to stay true to the concepts from Dan North's Introducting BDD blog, and as such, everything is done using the given, when, then style of specification. The way it is implemented allows you to have multiple givens and even multiple when's, and they should be executed in order (still checking into this).

Additionally, there is a full suite of Should extensions included directly in Given. This enables things like the ShouldEqual() call seen above, but is full of nice methods for collection comparison and type comparison, etc. For those of you familiar with MSpec, i basically ripped them out and made some modifications to make them work outside of MSpec.

The payoff, though, I think, is in the reporting. The test runner is filled with the scenario you've created, so that at a glance you can get details about what each test is actually doing without diving into the code: Test Runner

Additionally, an HTML report is created using t4 templating based on the results of the tests for each assembly. Classes with matching stories are all nested together, and each scenario name is printed for quick reference. For the above tests the report would look like this: Report Example

Failed tests would be colored red and can be clicked to view the exception details.

That's pretty much it. I'm using it in several projects I'm working on, so it is still being actively developed, but I'd describe the core as pretty stable. I'm looking at a way to share contexts by composition instead of inheritance, so that will likely be one of the next changes coming down the pike. Bring on the criticism. :)

nathan gonzalez
  • 11,817
  • 4
  • 41
  • 57
0

You could use NUnit.Specifications and write tests like this:

using NUnit.Specifications;
using Should;

public class OrderSpecs
{
    [Component]
    public class when_a_customer_places_an_order : ContextSpecification
    {
        static OrderService _orderService;
        static bool _results;
        static Order _order;

        Establish context = () =>
        {
            _orderService = new OrderService();
            _order = new Order();
        };

        Because of = () => _results = _orderService.PlaceOrder(_order);

        It should_successfully_place_the_order = () => _results.ShouldBeTrue();
    }
}
Derek Greer
  • 15,454
  • 5
  • 45
  • 52
0

MSTestEnhancer may help you, and you can get the package through NuGet.org.


Here is the sample code:

[TestClass]
public class TheTestedClassTest
{
    [ContractTestCase]
    public void TheTestedMethod()
    {
        "When Xxx happens, results in Yyy.".Test(() =>
        {
            // Write test case code here...
        });

        "When Zzz happens, results in Www.".Test(() =>
        {
            // Write test case code here...
        });
    }
}

And when you see your test result, you'll get this below:

Test results

I have written a post to present more information about it. See Introducing MSTestEnhancer to make unit test result easy to read - walterlv for more details.

walterlv
  • 2,366
  • 13
  • 73