Building a solution

By Dries Marckmann

Today our team leader asked me to get someone in our team started on a new solution. This entails 2 things:

  1. A dev tree
  2. Tests and code (and yes, I choose this order on purpose)

 

The Dev Tree

Suppose you have to make a new solution.

 

  • Create root:
    • $/TeamProject/<SolutionName>
  • Create main branch:
    • $/TeamProject/<SolutionName>/Dev
  • Create lib & src folder:
    • $/TeamProject/<SolutionName>/Development/lib
    • $/TeamProject/<SolutionName>/Development/lib/Unity
    • $/TeamProject/<SolutionName>/Development/lib/EnterpriseLibrary
    • $/TeamProject/<SolutionName>/Development/src
    • $/TeamProject/<SolutionName>/Development/src/Applications
    • $/TeamProject/<SolutionName>/Development/src/Framework
    • $/TeamProject/<SolutionName>/Development/src/Tests/Unit Tests
    • $/TeamProject/<SolutionName>/Development/src/Tests/Integration Tests

In the ‘scr’ folder you create the solution with the solution name. In the the solution you should create the same solution folder structure as on the file system, just to keep things easy to find. Be careful though, when you create new projects, Visual Studio doesn’t automatically set the project in the same folder as the solution folder you created it in.

Keep your structure in the tests the same as the structure of the ‘real’ projects. So if you have:

Project.Naam/Visitors/PaymentVisitor.cs

Than you should have:

Tests/Unit Tests/Test.Project.Naam/Visitors/PaymentVisitorTests/

Keep your project name the same as the assembly name. So if you have TeamProject.Billing.Framework.dll, your project name should be TeamProject.Billing.Framework.

This would also be a good time to add an automatic build. We use FinalBuilder which is an excellent tool – Dennis did an excellent job explaining why -  and we created a template for making automatic builds. This speeds up the creation process and that should help with the adoption of continuous integration and nightly builds. Generally it should help adoption of a strive towards better quality.

Now add tests and code…

Tests and Code

As far as I am concerned a new solution has at least 1 project: a Test Project. I don’t care that you don’t have any code (yet), as long as you have tests. Why? Having tests, even if they fail or even just don’t build, means you have thought about what it is you have to build to get what your ‘customers’ want. It should be the first project you create. Is this Test Driven Development (TDD)? Maybe. But I like to think of it as Behavior Driven Development (BDD). Personally I think BDD is somewhat easier to do than is TDD, because it gives you a much better starting point for your tests: the behavior requested by the customer. If you want to know about BDD I suggest this presentation by Dan North from about 14 minutes onward. (I suggest you start at the beginning and let Domain Driven Design sink in as well. I think DDD and BDD are a good match.)

I realize that I am going to have to give you an example of where to start. So here it comes.

In December 2009 someone from the sales department comes up to me and says: ‘I finally have everything ready for the price changes for 2010. In 2 days we have to get the letters out to all the customers or we will be to late. Here is the excel sheet with all the data. Oh and when this column is empty we should do blah and when this column has blah we the price should be yada yada yada…’ Right… I think you realize how I felt. I had 2 days to write some tool that would read the data from excel and get out the correct prices for 2010. There was very little time for manual testing by the sales team. They could do this just once. There just wasn’t anymore time. How did I feel? I was  excited! This was an excellent opportunity to use BDD/TDD and find out if it is as good as it claims it is.

First thing I did – after creating the dev tree as shown above – was create the Test Project. Then I started to do some thinking. (Don’t skip this step, it is important!) Every row in the sheet was a single price instance in our system. I created a test called Does_ReturnPrice_When_FedARow.. I admit, the name wasn’t optimal, but I had no clear idea of what I was going to build either. The test had a single Assert statement that verified if a not existent object was of type Price. Needless to say this didn’t compile.

I quickly added a Framework project called PriceChanges in which I added a Linq2Sql model with the Price class and added a class called ‘PriceChanger’ that could act as the ‘service’ that is going to do the work. The method under test was to be

ConvertToPrice(Row row)

I immediately  stumbled upon a problem. It is a problem which has everything to do with control. Control is important if you are writing unit tests. I had little control over the Row instances of Excel. I could have used TypeMock Isolator to gain control, but I think there is something to be gained from keeping your tests simple. So I designed for testability… I created a class called RawEntry and used that as input for the ConvertToPrice method. I could than start writing more tests and implementing the behavior in the method.

Along the way I realized I needed to acces the database to check the existing prices already there. It is hard to test linq2sql queries, for you have to fake your entire model. So I wrote a small Repository which was based on the Repository ScottGu created for MVC in the NerdDinner project: it is simple and easy to fake. In effect I created a wrapper around the operation that was hard to fake You can do the same for ‘supporting services’ if you need to.

In the same way I created a technical service that read the excel sheet and returns a List<RawEntry> and tested that. Writing the actual application in the Application folder was very easy now. It looked like this:

 

string filename = …

ExcelReader reader  = new …

PriceChanger changer = new …

PriceRepository repos = new …

List<RawEntry> list = reader.Read(filename);

foreach (RawEntry item in list)

{

     Price price = changer.ConvertToPrice(item);

     repos.Save(price);

}

 

We ran the code against a test database and had the sales department check it. They came back with the following remark: ‘It is all good… really… but we’d like something extra.’ For the extra behaviro we created extra tests and than we adjusted the code of the PriceChanger to implement the extra behavior. This only took about 30 minutes. So within 2 days we created a tool that did exactly what it should and added extra features in just 30 minutes. Tests are great!

Comments: 0

Leave a Reply

Your email address will not be published. Required fields are marked *