A story about the weather

This is the second blog post of my project WaterMyPlants. You can find the first blog post here

Introduction

This time, I just wanted to share a little about the process of going through one task in TFS from the very beginning until the task was pegged “done” – all using BDD and TDD, of course – there is no other way!

image
Image 1: The task for Collecting weather data from api.yr.no is now resolved

Since I am the only developer on this project, and also project owner, the project architect, and project code monkey, my scrum board tends to look like the teeth of a shark. My backlog is wafer-thin, and the user stories often come about AFTER the tasks have been described. Normally, you would have a well-defined backlog of user stories and for each sprint, a set of tasks for every user story planned.

User Story and Scenario

In order to know where to start, I always and without exception define scenarios for the task at hand based on the user story. This time, the story was about collecting wetaher data from an external source – I do not own a weather station, so the next-best thing is to grab the weather from a trusted source: 

The user story


@story62
Feature: There is a component that can retrieve weather information from Yr.no

	As a system developer
	I can obtain information about the local weather from api.yr.no
	So that I can find patterns between plant thirst and outside weather
 
@task61 @performsIO
Scenario: The component retrieves data from api.yr.no based on provided GPS location
	Given that the system has implemented weather retrieval component for Yr.No
	When I ask the system to retrieve weather data for my gps position
	Then the system responds with some arbitrary weather data

Code: User story 62 is about getting weather information from an external source

The tag performsIO needs some explanation: What it does is basically tell SpecFlow to use a runtime configuration prior to running this scenario. I use Structuremap as my IoC container to keep things simple.

[BeforeScenario("performsIO")]
public void BeforeMockedEnpointsScenarios()
{
	ObjectFactory.Initialize(init => init.AddRegistry(new WebRuntimeRegistry()));	
}
I usually have one “real” registry that I provide the applications with, and one registry 
where all the IO performing classes are replaced with mocks.
 

 

Implementing the scenario

All that was left to do was to write the code for each scenario step and “discover” 
my components methods: 

[Given(@"that the system has implemented weather retrieval component for Yr\.No")]
public void GivenThatTheSystemHasImplementedWeatherRetrievalComponentForYr_No()
{
   // No code required for this step
}

[When(@"I ask the system to retrieve weather data for my gps position")]
public void WhenIAskTheSystemToRetrieveWeatherDataForMyGpsPosition()
{
    var collector = ObjectFactory.GetInstance<IWeatherCollector>();            
    var measurement = collector.GetWeather(new GpsLocation());
    ScenarioContext.Current.Set(measurement);
}
[Then(@"the system responds with some arbitrary weather data")]
public void ThenTheSystemRespondsWithSomeArbitraryWeatherData()
{
    var weatherMeasurement = ScenarioContext.Current.Get<WeatherMeasurement>();

   // Inspection… 

}


I often write steps that do not require code. This is simply because I deem the scenario text 
to be a first class citizen, and seeing as it promotes understandability of the system, I 
opt for verbosity.
 
The GpsLocation class defaults to use the longitude and latitude of my house, which is why it 
only requires instanciation. 
 
Lastly, the inspection step simply verifies that the values of the weathermeasurement look ok. 
I’ve hidden the implementation details behind the comment “// Inspection”
 
 

The Test Driven part

BDD has now driven forth the need for an IWeatherCollector with the method GetWeather(GpsLocation). Additionally, it also specifies that it the collector needs to return some form of WeatherMeasurement class.

It is time to bring out the SOLID principles from the closet and do some test driven work. I will spare you the code details (do email me if you want them), but here are some of the points that I would like to make:

image
Image 2: Test explorer could use a “document order” sorting option

As you can tell, I am using Roy Osherove’s naming system for my unit tests. Since I see these as micro-specifications, I just love the way these work as fine-grained API documentation for the method I just described. Unfortunately, Visual Studio does not show me the tests in document-order!

This would give the most meaning to anyone looking at the unit-test explorer. A workaroundis to hit the shortcut-key ctrl+m+o between two unit tests in VS2012 to contract all methods:

image
Image 3: Collapsed view of my unit tests using Roy Osherove’s naming convention

Reading the unit-tests in document-order is, in my view, the best way to understand a method.

TDD is also about discovering contracts

Following the SOLID principles of object-oriented design, it quickly became clear during the implementation of  IWeatherCollector that the Single Responsability Principle drove me to use the following contracts: 

image
image
Image 4: Requirements of the WeatherCollector class identified during TDD

Why: The WeatherCollector class is no more than a service class that requests some xml from an external source and then translates that xml into a WeatherMeasurement object. The single responsability of the collector, is simply to combine the result of a reader and pass it along to the proper converter if all looks ok. This could possibly lead to using the name WeatherRepository instead, however, Collector was a term I felt more at ease with. Readability > patterns!

I won’t bother you with the details, but the implementations of the WebReader and Converter followed the same TDD approach – one after the other.

Geen tests lead to green story!

So, with all the specifications (unit-tests) now passing, and no more System.NotImplementedException() I ran SpecFlow to see if I had forgotten anything else:

image
Image 5: All done from SpecFlow. The user story was implemented as specified

General approach: Once you’re done with all the unit-tests for a method, you should run the user story to see if you have forgotten anything else. In my case, I only had one scenario, but I found out twice that I had to implement WebReader and then IConverter<XElement, WebMeasurement>. If you forget one, the SpecFlow report shines in red, telling you that sometihng is not yet implemented.  
– Do you still wonder why I love BDD?

image 
Image 6: Looking at the details of the scenario; the real output from api.yr.no

Conclusion

In this second post, I wanted to share with you how I use BDD and TDD to drive code:

  • Drag the task to “In progress” on your scrum board
  • Find or create the SpecFlow feature file for the user story that the task belongs to
  • Write one or more scenarios required to deliver that single task
  • Implement the scenario steps and drive forth the interfaces, their methods and objects required by the task. One scenario at the time! Do not implement classes yet, just let the interfaces sit until you’re done writing the scenarios.
  • Once done with one scenario, proceed to implementing the interfaces and objects using TDD
    • Create the classes
    • Implement the methods using TDD
    • Discover additional objects and contracts, but do not implement them before you’re done with the method you’re working on.
  • Every time you’re done with the unit-tests for a method, run a complete SpecFlow report and see if you have more scenarios to implement
    • Implement the missing scenarios, one at the time until report is all green
  • On the scrum board, drag your task from “In progress” to done
  • Pick the next task

 

Writing this blog post took me far longer than actually doing the steps above. Without the blog post, I would’ve been done within the hour.