January 2008 - Posts

Understanding what are you working on is the priority #1. If you not sure, then you don't know what are you working on. How do you keep several items linked logically while working on them - you try to keep something common between them. To be more particular, I will bring an example of an application I was exposed to with my team, and show several approaches, including the one I am so against.

The application has several sections. Each section has its' views. All the views are a part of the MVP pattern. One of the sections is called Product Management, where you can list all Product Releases in the system for a given Product, or create a new Product Release, or update an existing one. First image will show how the section file structure looks like.

Note: the team is using R# and is trying to be compliant as much as possible to all its' requirements (such as folder structure should be driving the namespaces, etc).

image

 

First thing first. ProductManage - sounds good, but DM? As I learned it was an abbreviation of "Domain Model" - but wait a minute, there's view and presenter exist in that same folder as well, so it's no longer just DM.

IViewEdit - bad. Not only it twist the logical naming of the class, but also copies the name of a different contract that belongs to a different section... and only because they partitioned by folders, they do not collide in name. They don't? Yes they do! When you work on those, you want to know what view contract is that. Not mentioning the inverted logic naming convention (IViewEdit, ViewEdit, IModelProduct, ModelProduct, DtoProduct, etc. - Long live Hungarian notation?!). After a few months a name like IProductReleaseEditView will ring a bell faster than IViewEdit, and then digging in what folder/sub-folder it lives in.

To myself this is a slavery to VS.NET and file system.  Rather than trying to arrange all views, models, presenters nicely in a solution window, we should care about meaningful names classes/interfaces are given. Besides, R# is not looking for the file name, it is looking for the class/interface name on a search. And I would definitely put more trust in re-factoring tool, rather than in file editor... but these are personal preferences.

I was looking at anonymous delegates today in .NET 2.0 and thinking how much "syntactical noise" it has and how clean and delicate it is with Lambda expressions. Remember though how it used to be?

obj.SomeEvent += new EventHandler(HandlerMethod);
private void HandlerMethod(object sender, EventArgs e) {}
obj.SomeEvent += new EventHandler(delegate(object sender, EventArgs e) {  });
obj.SomeEvent += delegate(object sender, EventArgs e) { };
obj.SomeEvent += (sender, e) => Console.WriteLine("nice!");

Looking for some feedback from ASP.NET community - are you using ORM mapping tool and how disconnected your business logic from the persistence during initial development?

To give a hint, I was looking at NHibernate, Microsoft Entities Framework, and in-house grown entities framework based on DB schema-first as opposed to the pure ADO.NET direct access. Thanks.

This is a non-technical post (contains no code).

"The size of disappointments is proportional to the size of expectations." -- translation from Hebrew (גודל האכזבות כגודל הציפיות). This is one I have to write down and stick in front of my eyes. I was expecting my team members to pick up the bug I got not so long ago on doing unit testing, TDD, DDD, Patterns and Principles, automation, pragmatic approach, etc. As much as I was expecting them to pick up the stuff and run with it, that much I was amazed to see that it is not going to happen. What is my conclusion? Don't over-expect (as over-design), don't ask what-if, act based on what-now. And the most important - don't have expectations for others, only for yourself.

Why do I post it? Because some good people are going through similar hoops in their development careers and looking for some sanity. So Mr. Mo - don't expect from the "great and mighty" what you have pictured to yourself, but picture yourself what you want to be and strive to that.

Myself? Stick to the right people, do not hesitate to challenge yourself and others, knowing that challenging others might not answer the questions and and you will have to either live with those unsolved, or do whatever it takes to get the answer.

The goal of the application is to allow specifications for search (criteria’s) to be required viewspecified by the client in order to perform a custom search. Figure 1 demonstrates the requirement. I intestinally keep it simple for the sake of the exercise.

Where do I start?

This is probably the most difficult question – where do I start? From the beginning, of course. I will try to make it sort of TDD way, and keeping the Agile concepts in head to respect some of the OO principles I have learned lately – no over-designing.

So what’s the plan? The plan is to have a plan! (Valiant cartoon, recommended). What do we have?

  • Criteria
  • Search results provided by some service based on criteria that customer has provided

But what if criteria are wrong? We should be able to handle it

  • Error message if criteria are wrong

Now the question – should the service for search result handle the validity of criteria? Nope, it only should consume it as-is, trusting it to be valid. Therefore Criteria has to be an object with its own behavior and “business rules” around it, that can be tested and become a dependency for the search service. Let’s hit the design through the tests.

My first tests are all about the SearchService component. The concern of this component is to invoke the model to bring some data based on SearchCriteria. I can almost smell 2 different dependencies it will rely on:

  1. Model
  2. SearchCriteria

I will not expand on the Model due to the complexity of the entire exercise and my limited knowledge at this point on Domain Driven Design, but will definitely come back to this subject sooner or later, as it seems to me THE way to write complex logic applications. I will cover SearchCriteria to make the example work.

SearchServiceTest as showed in Listing 1

  • Sanity check – can we get the object at all? --> Should_be_able_to_instanciate_service() – state based test
  • Given a certain SearchCriteria as a dependency, will the system under test (SUT) leverage the dependency, i.e. will be SearchCriteria used when SearchService is required to return result --> Should_be_able_to_return_search_results_with_a_given_search_criteria() – interaction based test using mocked dependency

Later, when SearchCriteria is tested and implemented, we add more tests to SearchService

  • Is NullSearchResult object (Null Object pattern) returned on an invalid SearchCriteria as a result of min date being bigger than the max date --> Should_return_empty_search_result_due_to_bad_dates
  • Is NullSearchResult object (Null Object pattern) returned on an invalid SearchCriteria as a result of min status being bigger than the max status--> Should_return_empty_search_result_due_to_bad_dates()

Listing 1

  [TestFixture]
  public class SearchServiceTest
  {
    private MockRepository mock;
 
    [SetUp]
    public void Setup()
    {
      mock = new MockRepository();
    }
 
    [TearDown]
    public void TearDown()
    {
    }
 
    [Test]
    public void Should_be_able_to_instanciate_service()
    {
      ISearchService sut = CreateSUT();
      Assert.IsNotNull(sut, "failed to instantiate service");
    }
 
    [Test]
    public void Should_be_able_to_return_search_results_with_a_given_search_criteria()
    {
      ISearchCriteria mockSearchCriteria = mock.CreateMock<ISearchCriteria>();
      ISearchService sut = CreateSUT(mockSearchCriteria);
 
      using (mock.Record())
      {
        Expect.Call(mockSearchCriteria.IsValid()).IgnoreArguments().Return(true);
      }
 
      using (mock.Playback())
      {
        ISearchResult result = sut.GetResults();
        Assert.IsNotNull(result);
      }
    }
 
    [Test]
    public void Should_return_empty_search_result_due_to_bad_dates()
    {
      ISearchService sut = CreateSUT(new SearchCriteria(DateTime.Today.AddYears(1), DateTime.Today, 0, 0));
      Assert.AreEqual(SearchResult.NullSearchResult, sut.GetResults());
    }
 
    [Test]
    public void Should_return_empty_search_result_due_to_bad_statuses()
    {
      ISearchService sut = CreateSUT(new SearchCriteria(DateTime.Today, DateTime.Today, 3, 2));
      Assert.AreEqual(SearchResult.NullSearchResult, sut.GetResults());
    }
    
    private ISearchService CreateSUT(ISearchCriteria searchCriteria)
    {
      return new SearchService(searchCriteria);
    }
 
 
    public ISearchService CreateSUT()
    {
      return new SearchService(new SearchCriteria(new DateTime(), new DateTime(), 0, 0));
    }
  }

 

SearchCriteriaTest as showed in Listing 2

  • Sanity check --> Should_be_able_to_instanciate_search_criteria()
  • Test validity based on criteria parameters (the rules I came up with are as long as minimum is less or equal to the maximum, criteria is considered to be valid) à Should_be_able_to_return_search_criteria_validity(int minYear, int minMonth, int minDay, int maxYear, int maxMonth, int maxDay, int minStatus, int maxStatus, bool result) df- in this test case I am utilizing the MbUnit’s ability to run same test with different input values

Listing 2

[Test]
    public void Should_be_able_to_instanciate_search_criteria()
    {
      ISearchCriteria sut = CreateSUT();
      Assert.IsNotNull(sut, "failed to instantiate service");
    }
 
    [RowTest]
    [Row(2008, 1, 1, 2008, 1, 15, 0, 0, true)]
    [Row(2008, 1, 15, 2008, 1, 1, 0, 0, false)]
    [Row(2008, 1, 1, 2008, 1, 15, 1, 3, true)]
    [Row(2008, 1, 1, 2008, 1, 15, 3, 1, false)]
    public void Should_be_able_to_return_search_criteria_validity(
         int minYear, int minMonth, int minDay, int maxYear, int maxMonth, int maxDay, 
         int minStatus, int maxStatus, bool result)
    {
      DateTime minDate = new DateTime(minYear, minMonth, minDay);
      DateTime maxDate = new DateTime(maxYear, maxMonth, maxDay);
      ISearchCriteria sut = CreateSUT(minDate, maxDate, minStatus, maxStatus);
      Assert.AreEqual(result, sut.IsValid());
    }
 
    private ISearchCriteria CreateSUT(DateTime minDate, DateTime maxDate, int minStatus, int maxStatus)
    {
      return new SearchCriteria(minDate, maxDate, minStatus, maxStatus);
    }
 
    public ISearchCriteria CreateSUT()
    {
      return CreateSUT(new DateTime(), new DateTime(), 0, 0);
    }
  }

So what do I have so far:

  1. Design for SearchService and SearchCriteria based on practical usage
  2. Design by Contract of the listed above
  3. Principle of Dipendency Injection (SearchCriteria is a dependency for SearchService) and Inversion of Control
  4. Dependency on abstraction and not concrete type for search criteria
  5. Encapsulation of business rules around searching criteria in an object
  6. Distinguished separation of concerns – SearchService knows nothing about SearchCriteria details and nuances, except what it should know – is criteria valid or not.

The implementation of the listed below classes and their contracts (interfaces) in Listing 3, Listing 4, Listing 5, and Listing 6 are entirely based on the tests I conducted. This ensures not only that the code is tested, but also documents well what should be the expected behavior, i.e. an alternative documentation for the design.

 

Listings 3-6

public interface ISearchService
  {
    ISearchResult GetResults();
  }
 
  public interface ISearchCriteria
  {
    bool IsValid();
  }
 
  public interface ISearchResult
  {
    // details are omitted to simplify example    
  }

public class SearchCriteria : ISearchCriteria
  {
    private readonly DateTime maxDate;
    private readonly int minStatus;
    private readonly int maxStatus;
    private readonly DateTime minDate;
 
    public SearchCriteria(DateTime minDate, DateTime maxDate, int minStatus, int maxStatus)
    {
      this.minDate = minDate;
      this.maxDate = maxDate;
      this.minStatus = minStatus;
      this.maxStatus = maxStatus;
    }
 
    public bool IsValid()
    {
      return DatePartOfCriteriaIsValid() 
             && StatusPartOfCriteriaIsValid();
    }
 
    private bool StatusPartOfCriteriaIsValid()
    {
      return minStatus <= maxStatus;
    }
 
    private bool DatePartOfCriteriaIsValid()
    {
      return minDate <= maxDate;
    }
  }

  public class SearchService : ISearchService
  {
    private readonly ISearchCriteria searchCriteria;
 
    public SearchService(ISearchCriteria searchCriteria)
    {
      this.searchCriteria = searchCriteria;
    }
 
    public ISearchResult GetResults()
    {
      if (searchCriteria.IsValid())
      {
        return new SearchResult();
      }
      return SearchResult.NullSearchResult;
    }
  }

 

Tools used

  • MbUnit
  • Rhino.Mocks
  • NAnt
  • Visual Studio 2008
  • Console2

NAnt and automated build

I wanted to have my build script files to be partitioned and structured in a manner where I can invest minimum of the effort to kick off a new solution or a project in a solution, and being able to configure automated build (and tests) fast. The scripts are not the best, but this is just the first attempt to make it happen. The only targets I was actually running were “build” and “test”.

Usefulness of this post

Someone said to me that what you just learned today might be looked by someone else tomorrow. In absolutely no way I am trying to teach people what have just barely learned. The motivation is to get others inspired and trigger feedbacks in order to evaluate what I am proposing.

Thanks

  • JP Boodhoo for introducing myself to the world of patterns, TDD, and DDD
  • Glen for opposing ideas I had and pushing to prove that they are not just words
  • Mr. Mo for ideas and encouragement to publish the post
  • Adam for fuelling me up on the NAnt cryptic style and partitioning of it in general

 

Attachment note: to keep file small, I have wiped the dlls and exes from the Tools directory. You will have to download those and add in the right folders. Sorry for that.

Since code is sort of trancated, I also attach the original version of the post in a PDF format.

I am working on automated builds for our projects. I am quite excited about it, since it feels like taking back the power over the creation of the code. Not only that, the 'auto-magic' dissolves ones you do it manually and things become simple. You run the script, the script is failing, you need to fix the issue and you want the issue to be simple in order to A) locate it quickly B) fix in the least effort applied.

Saying that, I have to admit that having a strong build scripts takes time to develop as well. Thanks to the (NAnt/NAntContrib) community this is not a difficult task even for a novice like myself. And lets get back to the concept - simplicity.

If you look around, lots of build scripts are a single file that is doing it. But isn't this a sort of violation of SRP? One of the projects' build file in the entire solution should not be too overwhelming to read just because there's a bunch of projects and global properties. So I decided to partition.

In my case the partitioning is working great (master build file and child build files). Parent build file acts as a trigger for each individual project, invoking the right target.

This is great even with the projects that have dependencies on other projects in solution. The old plain CSC is doing it all.

Web Application Project (WAP) - an absolutely different creature. I failed to launch WAP compilation with aspnet_compiler. So I used msbuild to compile the web application project and trace what it was doing. To my surprise it leveraged csc.exe compiler to do the job. When the web application was requested in browse, the aspnet_compiler kicked in. So does that mean there is no need in aspnet_compiler to compile a web site?

PS: I will make a separate entry on Partitioned build files.

Update: please read my comment below. I have found a temporary (and maybe a permanent) way to achieve the goal with WAP compilation.

Combining client-side with server-side, coping with multi-browser support, handling imperfect world of CSS , implementing the code the best way you can relaying on patterns and principles with legacy code kicking around, keeping up with the pace of changing, facing clients that are not ready to pay for the quality, dealing with team mates that are still loyal to procedural code order and married to databases, yet not over-designing is more than just agile. My definition of this is going on the edge with agility in mind. Or just Edgility.

Just finished reading Agile Principles, Patterns, and Practices in C# from Robert and Micah Fowler Martin. A definitely recommended book. Some stuff is not taking advantage of the .NET (not using generics where could do that, Visitor pattern for example.

Next book - Domain-Driven Design: Tackling Complexity in the Heart of Software. What else would you recommend that would fall into this category?

I was following the development on Script# CCC was developing. Sounded like an interesting idea of getting closer .NET and client-side JavaScript. Today found a link to MS experimental project called Volta (hmm, I wonder what will happen if you dare to touch it, same that happens to many experimental projects - a moment of joy and a big shocker at the end? :)

In my previous blog I was looking how to enable MbUnit with MSBuild from visual studio. As nice as it is, I was missing the flexibility and options of going beyond unit testing only.

After attending "Nothing But .NET" training session help by JP Boodhoo in Calgary, I was more aware about option of using NAnt and CruiseControl.NET in the future for potential Continuous Integration.

So why NAnt? Several reasons:

  • I was exposed to NAnt first
  • Simple XML like syntax that makes total sense
  • Lots of resources available from the OSS community

As a beginner the first task was to make it work. Downloading the latest from the official site for NAnt, reading how-tos, and... not working. As a Visual Studio grown developer with addictions to Intelisense and mouse, I straggled to build a simple NAnt build file (script file interpretated by NAnt to perform tasks/operations) without copying it entirely from somewhere. Solution - keyboard can replace mouse, but Intelisense - nothing. Google has brought quiet a few results. What I loved in Kevin's solution was to teach Visual Studio about NAnt Intelisense using NAnt itself (nice idea!). What it does is basically dumps NAnt schema file into Visual Studio schemas repository folder. The great part about this approach is full automation of the process plus the ability regenerate the schema with updates, such ad NAntContrib (additional tasks that NAnt can do if you use it), in a painless way my 4 years old son could do. The simplified version looks like this:

<target name="build-nant-schema-for-vs2005" depends="load-nantcontrib-schema" description="Generate VS intelisense for NAnt">
<call target="load-nantcontrib-schema" />
<nantschema output="NAnt.xsd" target-ns="http://nant.sourceforge.net/release/0.86-beta1/nant.xsd" />
<property name="visual.studio.schemas.path" value="C:\Program Files\Microsoft Visual Studio 8\Xml\Schemas"/>
<copy todir="${visual.studio.schemas.path}" file="NAnt.xsd"/>
<delete file="NAnt.xsd"/>
</target>

 

Well, I slightly re-factored Kevin's example to be partitioned better. So the target "load-nantcontrib-shema" is just another target (think of it as a sub-routine in NAnt script) to update normal NAnt schema with NAntContrib schema.

<target name="load-nantcontrib-schema" description="Load NAntContrib tasks to generate schema">
<property name="NAntContrib" value="C:\Data\SandBox\ProjectAbc\Tools\NAantContrib-0.85\bin" />
<loadtasks assembly="${NAntContrib}\NAnt.Contrib.Tasks.dll" />
<echo message="load-nantcontrib-schema finished." />
</target>

So now producing the Intelisense for Visual Studio (2005 in this case) as simple as executing a normal NAnt script:

C:\Data\SandBox\ProjectAbc>nant load-nant-schema-for-vs2005

This is making sure that any NAnt script file (also called build file) that contains reference to the schema has intelisense. A typical build file starts like this:

<?xml version="1.0" encoding="utf-8" ?>
<project name="Proxy" default="help"
xmlns="http://nant.sourceforge.net/release/0.86-beta1/nant.xsd">

What that default="help"? Help is one of the targets available in this NAnt script and if no target is provided in command line as an argument, the default is used, "help" in this case. I have picked this idea from the book that describes the NAnt in a very quick and practical way - Expert .NET Delivery Using NAnt and CruiseControl.NET. The idea is to have default target that will list all possible targets in script providing some help to people that did not build it, but have to use (the most important 'ility' - maintainability, isn't it?!)

C:\Data\SandBox\ProjectAbc>nant
NAnt 0.86 (Build 0.86.2898.0; beta1; 08/12/2007)
Copyright (C) 2001-2007 Gerry Shaw
http://nant.sourceforge.net

Buildfile: file:///C:/Projects/Spikes/Proxy/default.build
Target framework: Microsoft .NET Framework 3.5
Target(s) specified: help


help:

[echo] Available Targets:
[echo] build-nant-schema-for-
vs2005
[echo] clean
[echo] build
[echo] test
[echo] run

BUILD SUCCEEDED

Total time: 0.1 seconds.

Great. Now we need to pump the script contents and run it. But I am too lazy to leave the natural environment of inhabiting - Visual Studio. And even a cool tool like Console2 that is outstandingly great for this purpose, still doesn't make me feel like leaving the cave. So there's an add-in for Visual Studio 2005 called VSCmdShell (and 2008 according to authors, but never worked for me) that can give you the command shell as a part of Visual studio, where you can execute your NAnt build script with parameters (targets). Now editing, building and testing can be all done under one hood in peace and harmony - I heard hallelujah?.

The next step - start building the targets to do cleaning, building, testing, and running. Deployment might be an option as well. I am in the beginning of my NAnt climbing curve, but it definitely looks great so far. Happy climbing!

 

PS: I added a video to demonstrate how VSCmdShell is playing nicely in VS.NET 2005 in combination with NAnt. My computer was about to die and Windows Media Encoder was not the best choice for encoder, but this is what I had, with this had to win :)

More Posts