Following on from my last post I now have a nice failing scenario in Spec Flow
I could just rush in and start writing production code to make the scenario pass, but this would be a mistake, scenarios by their nature tend to be at a higher level covering a flow of work through the whole system, so I will probably have to write a ‘lot’ of code to make the scenario pass, this is not very incremental, I always like to have working code, so any changes or additions I make have to be small.
To achieve these small incremental changes I need a finer gained framework to guide and assist me, I have chosen Machine Specifications, primarily because I like its output, it produces sentence like structures making the reports very easy to read and the style fits well with the textural nature of the spec flow fixtures, also the framework forces a nice code structure, which makes the completed tests easy to read and understand. But any unit test framework could be used, the idea is that these smaller tests are there to support the scenario and feature tests.
A quick lap around Machine Specifications.
Unlike other frameworks Machine Specifications doesn’t include the word test, instead it uses the idea of asserting behaviour, and aims to produce a report that reads as discrete sentences that describe the expected behaviour.
The subject line is either some text describing the subject of the behaviour or a typeof(some class) statement.
The class name describes the behaviour we are testing, within the class there are three allowed delegate expressions Establish, Because and It.
Establish context is used to setup the environment that the objects will operate in, this includes the subject of the test.
Because of is used to call the single method, event or property that the behaviour dictates, this should be a single action, if multiple actions are required, they should be specified separately in different classes.
The It delegate is used to assert the results of the Because action, there can be one or more of these within each class, each should be a single line of code.
Running these specifications produce the following results.
This is where the Machine Specifications framework starts to shine, by careful naming of the subject, class and It clauses, a distinct description can be build simply by running the tests.
A live example
In my previous post I showed the feature and step definition files for a home project I was working on, the first start up scenario looked like this
the first and second asserts can be easily done with simple one liners, but from the third third line onwards requires that I read a solution file, and discover what projects are there and print out the names to the console, which is a bit more code than a single line of code, so at this point I dropped down to machine specifications.
I created a test solution file that I could use to within the tests this allowed me to control the paths to projects and other data, this solution file was added to the test project and set to always copy to the output directory during a build. It is always good to control the data coming into an application during testing as it ensures that noting external to the test can effect the results.
The above test creates a file path to this solution file, then loads the file and ensures that the solution name is set, I decided to create a solution object that would read the solution file and extract the required data using simple regular expressions, because the solution file is not actually valid xml, and cannot be easily parsed in another way.
After getting this simple test to pass, I continued in the same vain to extract the project file paths and names which are then passed back up to the main application for display making the scenario pass completely.
Having the feature file and its scenarios guides the generation of the lower level machine specifications, making the tests easier to write because at the time of writing you are attempting to solve a known problem, also the nature of the testing has changed, now only worrying about the output from a function or property rather than how that outcome is achieved, allows the internal structure of a class to be refactored without breaking the tests.
Using this outside in method of development achieves a higher test coverage of the code, with fewer tests, because the features provide the over all frame for the development process, leaving the machine specifications to perform a supporting role where details are necessary.
Knowing when to drop down to the lower level is important, I have formed a simple guideline that I follow
if I can’t make the scenario pass with a single line of code, either directly or by calling an already existing method, then I will drop down to the machine specifications.
Which ensures that I don’t go off writing production code without the assistance of the testing framework.