Development Blog

 Tuesday, September 02, 2008

It's been a while, but we've gotten several new things into Machine.Specifications (MSpec). I'm excited to finally release them for everyone to start playing with. You can grab the bits here.

Let's talk about what's new though. Here's an example of a new context/spec:

  [Concern("Console runner")]
  public class when_specifying_a_missing_assembly_on_the_command_line
  {
    Establish context = ()=>
    {
      console = new FakeConsole();
      program = new Program(console);
    };

    Because of = ()=>
      exitCode = program.Run(new string[] {missingAssemblyName});

    It should_output_an_error_message_with_the_name_of_the_missing_assembly = ()=>
      console.Lines.ShouldContain(string.Format(Resources.MissingAssemblyError, 
      missingAssemblyName));

    It should_return_the_Error_exit_code = ()=>
      exitCode.ShouldEqual(ExitCode.Error);

    const string missingAssemblyName = "Some.Missing.Assembly.dll";
    public static ExitCode exitCode;
    public static Program program;
    public static FakeConsole console;
  }

There have been a few semantic changes

  • The Description attribute has been removed. There is now an optional Concern attribute that allows you to specify a type and/or a string that the context/spec is concerned with.
  • Context before_each is now Establish context.
  • Context before_all is now Establish context_once.
  • Context after_each is now Cleanup after_each.
  • Context after_all is now Cleanup after_each.
  • When {...} is now Because of. This is closer to SpecUnit.NET's verbage, and doesn't force you to specify the "when" twice.

There is now a console runner

We don't quite have all the options we want yet, but the basics of the runner are working. Here's the help from the runner:

We also stole Bellware's SpecUnit.NET reporting stuff and ported it over. You can now generate a report on your specs with the --html switch. Here's an example run:

This is the report it generates.

Want to try it out?

  1. Grab the drop here.
  2. Extract it somewhere. Put it somewhere semi-permanent because the TestDriven.NET runner will need a static location for the MSpec TDNet Runner.
  3. If you want TestDriven.NET support, run InstallTDNetRunner.bat
  4. Check out the example in Machine.Specifications.Example. Note that you can run with TD.NET.
  5. Create a project of your own. Just add Machine.Specifications.dll and get started.
  6. Send me feedback! Leave comments, email me, tweet me, whatever.

Also, this is part of Machine, so feel free to take a look at the code and/or submit patches. There's also a Gallio adapter in there, but I didn't include it in the release as it's not quite polished enough yet. If you're interested in it, talk to me. Special thanks to Scott Bellware, Jeff Brown and Jamie Cansdale for their help and support. Also, extra special thanks to Eleutian's newest dev, Jeff Olson for much of the recent work that has gone into MSpec!

by Aaron on Tuesday, September 02, 2008 1:49:51 PM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Tuesday, July 01, 2008

Mikel Lindsaar recently posted a tip encouraging rSpec users to not use before :each, and set up the context in every "it" specification.

I'm afraid I disagree. By pushing context setup into your specifications, you're allowing your contexts to become artificial and anemic and your specifications to become fat and more than just specifications.

Ultimately, this means that your reports will read poorly and it will be easy to introduce specifications in a context that do not match the others.

Mikel arrives at the following specs at the end of his post:

describe "when not logged in" do
  it "should redirect if we are not logged in" do
    get :index
    response.should redirect_to login_path
  end
end

describe "when logged in" do
  def given_a_logged_in_user
    session[:logged_in] = true
    session[:user_id] = 99
  end

  it "should let be a success" do
    given_a_logged_in_user
    get :index
    response.should be_success
  end

  it "should render the index template" do
    given_a_logged_in_user
    get :index
    response.should render_template('people/index')
  end
end
"when logged in" is not what I would consider a valid description of Mikel's context in these specs. I would call it something along the lines of "when visiting the index page while logged in". *That* is the context you are specifying against. Compare:

when logged in, it should render the index template

vs.

when visiting the index page while logged in, it should render the index template

The first is clearly missing something. Unless rendering the index template is a direct result of just *being* logged in, the spec is flawed.

With that in mind, as soon as you describe your context, there's no reason to not pull that context setup into a single before method. It forces you to use that context in every specification contained within your describe. It also makes your tests easier to read. You establish your context, and then you make one line specifications against that context.

I do agree that DRY should not be taken too far in tests. Base classes, helper methods, all that sort of thing can quickly obfuscate them, but do not forsake the context setup.
bdd | rspec | dry
by Aaron on Tuesday, July 01, 2008 7:04:07 PM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  |  Trackback