MSpec – Take 2
About a year ago, I remember first hearing about Machine.Specifications (github) and trying it out for something I was developing. I liked it right away, especially the way it cut away a lot of the noise involved with writing tests/specifications.
I never really ended up using MSpec on things at work. There were lots of lame reasons:
- We were using MbUnit and MSpec worked with NUnit (like I really care, seriously)
- We already had lots of existing tests and didn’t want the disjointed feeling of having two ways to do things
- I was already struggling at work getting people to adopt the new things I had already introduced into our environment.
I took what I could out of MSpec and the other Spec Driven Development stuff I got from the community and kind of rolled my own style of doing spec driven development just using MbUnit. I’ve since gathered that there are lots of people doing this.
Recently I had to do a small sample application in a short period of time. It wasn’t for work (i.e. no MbUnit legacy) and I still wanted to do it spec first, so I grabbed the latest MSpec and used that.
I noticed there are several small but appreciated changes that have been made since the last time I used MSpec, namely:
XUnit and (kind of) Gallio Support
To use Gallio/MbUnit or xUnit with MSpec, there’s really nothing special to do – just reference the testing framework you want to use and the adapter assembly.
The adapter assembly is the magic part of MSpec. It helps the unit testing framework build a model of the tests to run. When using MSpec, you’re not putting attributes on things anymore to indicate they are unit tests. The adapter is needed so the unit testing framework knows what to run as tests.
For NUnit and xUnit, there are a bunch of nice extension methods defined for doing systemUnderTest.ShouldXXX() style assertions. There aren’t any for MbUnit, but I made some in my fork of MSpec if you’re interested.
I imagine you could write an adapter for MSTest, as well as some extension methods, but my interest in MSTest-ifying MSpec this ends there.
Once you start using these extension methods, the unit testing framework dependencies kind of melt away:
public class when_pricing_an_item_that_requires_no_sales_tax : with_an_item_to_price { static Money price; Because of = () => { the_product.is_not_taxed() .when_shipped_to(our_destination); price = pricingService.PriceItem(1, the_product, our_destination); }; It prices_as_the_base_price = () => price.ShouldEqual(our_base_price); }
Where’s the test framework code? Does this use NUnit? MbUnit? The only assertion here is in ShouldEqual, which is one of those extension methods that calls who cares which unit testing framework.
That’s the real lesson here: who cares which unit testing framework you’re using – that decision doesn’t add any value to your product, and doesn’t make this a valid excuse for not using MSpec. If one framework or the other works better with your build process or some tool you are using, just use that framework.
Pending
public class when_pricing_an_item_that_requires_sales_tax_where_we_are_shipping : with_an_item_to_price { It includes_the_tax_in_the_price; It includes_the_tax_in_the_total_tax; It doesnt_include_the_tax_in_the_subtotal; It doesnt_include_the_tax_in_the_line_item_total; }
I saw Aaron demo this somewhere once, maybe at an #altnetseattle meeting or something, but I didn’t get it at first. I thought it was about going off and writing a bunch of Tests then implementing them. My view that you write one Test, then implement, then another, etc.
The difference is, when doing things Spec first, it’s…well…it’s just different. I think one reason I like one test at a time is that I am testing close to the implementation at that point. Since I am in the process of designing the system while doing TDD, I don’t know what the rest of the design is going to look like till I get there. If I write too many tests, I am speculating about what the design is going to look like.
Specs aren’t so much about the physical design of the system so they don’t change (as much) as the design evolves. If they do, they have good (read: business) reason. It’s not wrong to translate specs into code as part of the same process you are getting them (ex.: talking with a product owner…taking notes in the form of "public class whatever_context { It does_this; It does_that; …}".) That process is unlikely to be as granular as TDD tests, as no business person that is making money wants to hold your hand all day while they spoon feed you one thing at a time.
The output from running the above specs is:
when pricing an item that requires sales tax where we are shipping >> includes the tax in the price (NOT IMPLEMENTED) >> the tax in the total tax (NOT IMPLEMENTED) >> include the tax in the subtotal (NOT IMPLEMENTED) >> include the tax in the line item total (NOT IMPLEMENTED)
You can go ahead and spec things out ahead of time just saying "It does_whatever" and you’ll get a nice report of what’s to be done.
Behavior
This feature answers a question I’ve had since the first time I saw someone do BDD: What do you do when you want to specify the same behavior for multiple contexts?
[Subject("Calculating sales tax")] public class when_an_item_is_a_kind_of_food : with_an_invoice_with_one_item { Because of = () => invoice = new Invoice().WithSomeKindOf(Category.Food); Behaves_like<NoSalesTaxAdded> no_sales_tax_added; } [Subject("Calculating sales tax")] public class when_an_item_is_a_kind_of_medical_supply : with_an_invoice_with_one_item { Because of = () => invoice = new Invoice().WithSomeKindOf(Category.MedicalSupplies); Behaves_like<NoSalesTaxAdded> no_sales_tax_added; } [Behaviors] public class NoSalesTaxAdded { protected static Invoice invoice; It has_no_sales_tax_added = () => { foreach (var item in invoice.Items) invoice.GetPostTaxValueFor(item).ShouldEqual(item.Value); }; }
In the samples that come with MSpec, there is an example of two implementations of the same interface, like a TypeFixture in MbUnit.
It seems like, in the applications I work on, I rarely run across a need to use this kind of test, but I do sometimes need RowTest-like tests, or, more often, factory driven tests – like to specify that for a certain set of contexts, there is some behavior. My example above is a lame attempt to illustrate this.
What’s Next
That’s kind of the technical view of things. Hopefully I’ve gotten your attention and you are git clone-ing MSpec in the background right now. I hope to post part 2 of this in a couple of days, with some examples of how I use MSpec in real code.
This is great to see. I’ve taken the plunge into using MSpec so I’ll be hoping to see better ways of using it than my own ignorant neophyte attempts.
Thanks.
I am unable to use Gallio/MbUnit or xUnit with MSpec. I’ve created a new project with a spec and added the following references:
* Machine.Specifications.dll
* Machine.Specifications.GallioAdapter.dll
* Machine.Specifications.XUnit.dll
* xunit.dll
which are built from 2009-08-04 source code of Machine.Specifications.sln
ReSharper can see and run the spec, but no spec shows up in Gallio (3.0.6 build 787).
Am I missing something?
@Jimmy
I’ve only been using it with MbUnit/Gallio and I never used the GUI runner until just now (I usually only use TestDriven.NET.) I will look at it when I get a chance, and let you know if I figure anything out.