Getting Started with Specify

How to install Specify and write the first specifications.

I am assuming that you have a test project that you want to write tests in and that you are using Visual Studio. This could be a unit tests project for MsTest, or a class library for other types of test frameworks

Add NuGet package

Specify is available as a NuGet package. You can add it via the Manage NuGet Packages dialog or by the Package Manager Console.

To add Specify via the Manage NuGet Packages dialog, just right-click on References in your test project and select Manage NuGet Packages. From the dialog, search for Specify and then click the Install button on the Specify package.

903

To add Specify via the Package Manager Console, from the Menu select Tools > NuGet Package Manager > Package Manager Console, then type the following in the console:

728

NuGet will install Specify and the latest version of TestStack.BDDfy.

Choose a test framework

Like BDDfy, Specify can run with any testing framework. Well, NUnit, xUnit, MsTest and Fixie anyway, which are the ones I've tried it with.

If you are using Fixie, then you can use Specify as is, and you just have to provide a convention to tell it how to discover and run your specifications.

If you are using NUnit, xUnit, or MsTest, then you need to provide the attributes that they need to discover and run your specifications. The good news is that you only have to provide this information once, on your base class - all of the specifications that you write will inherit from this class and not need any attributes.

At this stage, I've decided not to release separate NuGet packages for each test framework. I don't think it's worth it for a few lines of code. To use NUnit, for example, you just need to copy this code into your project, which inherits from the two Specify base classes and adds the NUnit test attributes. You should use this as your base class that your specifications inherit from.

[TestFixture]
public abstract class ScenarioFor<TSut, TStory> : Specify.ScenarioFor<TSut, TStory>
    where TSut : class
    where TStory : Story, new()
{
    [Test]
    public override void Specify()
    {
        base.Specify();
    }
}

[TestFixture]
public abstract class ScenarioFor<TSut> : Specify.ScenarioFor<TSut> 
    where TSut : class
{
    [Test]
    public override void Specify()
    {
        base.Specify();
    }
}

I would love to find a way to avoid this step, by detecting the test framework being used and injecting the attributes. I suspect you might be able to do it with something like Fody, but at this stage I don't know of how to do this.

Choose a Container

Auto-mocking

If you want to use NSubstitute, FakeItEasy, or Moq for auto-mocking, then just install one of those packages from NuGet. Specify will look for them in that order and use the first one it finds as an auto-mocking container.

Autofac IoC Container

If you want to use an IoC container, then Specify has built-in support for Autofac. Just add an Autofac module and Specify will discover it and add its registrations. Specify will provide a child Autofac container for each Scenario.

Custom Container

Finally, if you want to create a custom container, such as one using a different IoC container or mocking framework, then just implement the Specify IContainer interface. You will also need to register it in the IoC container that you use. You can either register it directly, or implement the IDependencyResolver interface for your IoC container and override the CreateChildContainer method, which will be called before every Scenario.

Writing Scenarios

That's it! If it seems a bit complex, hopefully that will improve with time. Right now, there's a little bit more configuration than I would like.

Now you just have to write your Scenarios. Just write a class per scenario, implementing either:

  • ScenarioFor: TSut is the class that is being tested, and will be created for you by the container.
  • ScenarioFor<TSut, TStory>: TStory is the user story class.

For example:

public class DetailsForExistingStudent : ScenarioFor<StudentController>
{
    ViewResult _result;
    private Student _student = new Student { ID = 1 };

    public void Given_an_existing_student()
    {
        Container.Get<ISchoolRepository>()
            .FindStudentById(_student.ID)
            .Returns(_student);
    }

    public void When_the_details_are_requested_for_that_Student()
    {
        _result = SUT.Details(_student.ID) as ViewResult;
    }

    public void Then_the_details_view_is_displayed()
    {
        _result.ViewName.Should().Be(string.Empty);
    }

    public void AndThen_the_details_are_of_the_requested_student()
    {
        _result.Model.Should().Be(_student);
    }
}