Test Driven Development Built Name Generator Part 1

This is going to be a multi-part series on building a straight forward database driven name generator.  I’ve tried the random name generator thing and it generally isn’t so great.  I’d tried in the past this idea with the database table of census names and it works great.  So this is part 1.  I’ll post these entries consecutively over the next few days so stay tuned.

First I started a new clean solution and added a test project.  I figured I wasn’t even going to add the actual assembly project yet, just jump right in and start writing a test, get red, and go to the next step.

I added a test file and wrote the following test.

   1:  [TestMethod]
   2:  public void VerifyFullNameObjectInstantiatesWithNames()
   3:  {
   4:      FullName fullName = new FullName();
   5:      Assert.IsTrue(fullName.FirstName.Length == 0);
   6:      Assert.IsTrue(fullName.LastName.Length == 0);
   7:  }

After that I used ReSharper Alt+Enter Shortcut plus a little additional keying in myself to flesh out the skeletal class and get a green light.  I ended up with the class below.

   1:  public class FullName
   2:  {
   3:      public string FirstName { get; set; }
   4:      public string LastName { get; set; }
   5:  }

Next I wanted to enforce a contract so I could create a factory to build my FullName objects with.  With that change the interface and class I had now looked like this.

   1:  public interface IFullName
   2:  {
   3:      string FirstName { get; set; }
   4:      string LastName { get; set; }
   5:  }
   6:   
   7:  public class FullName : IFullName
   8:  {
   9:      public FullName()
  10:      {
  11:          FirstName = string.Empty;
  12:          LastName = string.Empty;
  13:      }
  14:   
  15:      public string FirstName { get; set; }
  16:      public string LastName { get; set; }
  17:  }

That gave me a green light on my first test.  After that I built a test for the factory that could build the names.

   1:  [TestMethod]
   2:  public void VerifyFullNameObjectReturnsFromFactory()
   3:  {
   4:      IFullName name = NameFactory.Build();
   5:      Assert.IsTrue(name.FirstName.Length > 0);
   6:      Assert.IsTrue(name.LastName.Length > 0);
   7:  }

I then took the NameFactory object and fleshed it out so I could build, run the test, and get green lighted.  Below is the NameFactory Class.

   1:  public class NameFactory
   2:  {   
   3:      public static IFullName Build()
   4:      {
   5:          return new FullName { FirstName = "TestFirst", LastName = "TestLast" };
   6:      }
   7:  }

So now I have a green light on the name factory.  But even though I have a green light, it doesn’t exactly do what it needs to do, which is get some good unique and random names.  Next step, write a test for getting back some random names.

   1:  [TestMethod]
   2:  public void VerifyFullNameIsRandom()
   3:  {
   4:      IFullName nameOne = NameFactory.Build();
   5:      IFullName nameTwo = NameFactory.Build();
   6:   
   7:      Assert.AreNotEqual(nameOne.FirstName, nameTwo.FirstName);
   8:      Assert.AreNotEqual(nameOne.LastName, nameTwo.LastName);
   9:  }

After creating this test, I have to dive a little deeper.  First I grabbed the census names for first and last names off of the Internet.  After that I added two projects to my overall Visual Studio Solution.  One is a database project and one is Windows App to use to manipulate the text file data and get it into our database.

Next I created the Generator Database a table to store the names that are stored in the files.

The SQL create script is shown below.

   1:  IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[Names]') AND type in (N'U'))
   2:  BEGIN
   3:  CREATE TABLE [dbo].[Names](
   4:      [NameId] [uniqueidentifier] NOT NULL,
   5:      [Name] [nvarchar](50) NOT NULL,
   6:      [Type] [smallint] NOT NULL,
   7:   CONSTRAINT [PK_Names] PRIMARY KEY CLUSTERED 
   8:  (
   9:      [NameId] ASC
  10:  )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
  11:  ) ON [PRIMARY]
  12:  END

I also added an extended property to outline how I intended to use the “Type” column.

   1:  IF NOT EXISTS (SELECT * FROM ::fn_listextendedproperty(N'MS_Description' , N'SCHEMA',N'dbo', N'TABLE',N'Names', N'COLUMN',N'Type'))
   2:  EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'1 = Male, 2 = Female, 3 = Last Name' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'Names', @level2type=N'COLUMN',@level2name=N'Type'

My intention is for the “Type” column to have a 1 for the male first name, a 2 for a female first name, and a 3 for the last name.

I’ve covered creating the initial tests and objects to use.  Also the database table that is needed and the create scripts have been provided.  Next steps are to build a quick app to get the names imported into the database table.  Stay tuned and that will be posted tomorrow.

Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

Posted by: adron
Posted on: 2/4/2009 at 7:20 AM
Tags: , , , , ,
Categories: How-To, Samples, and Such | Design Patterns | My Projects
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (2) | Post RSSRSS comment feed

Software, Success and Ranting

I've been extra active reading blog entries lately and have come upon a few that have really got me kind of wired.

Software Development Dogmata - Good Practices Gone Bad

The first one is this article by Daniel Pietraru (who I do not know) over on little tutorials "Software development dogmata - good practices gone bad".  There are few major issues I have with this slight skewing of facts.  Overall, excellent article, but there are some issues.

First is the "Agile != Flexible".  Read the manifesto, there is no such thing as Agile that isn't flexible.  The birth of agile that is mentioned is rather skewed too.  If you truly want to learn about Agile, and you have a motivation to have good, happy, satisfied employees that are highly productive, look up the motivations behind Agile.  Otherwise, don't.

Daniel then goes on to negate the benefits of a short development cycle.  Iteration based development has proven so effective almost every major process out there these days takes some form of this.  As for the statement he makes "you cannot demo a car engine until it is fully built" is patently wrong.  You CAN demo a car engine and that is the entire point behind computer models used solely to design engines, and cars in general, in a very LEAN or AGILE manner.  So you can deny it, but I believe these methodologies, the focus on individuals, individual motivation, individual enablement, and other such characteristics of lean, agile, and others is exactly why Toyota has stomped the competition is is basically the world leader in automotive manufacturing and quality.

One really can't deny Toyota's success, and it is the prime cut of what agile is.

Pair programming, he has a slightly decent point on this topic.  I too find myself torn about the usefulness of pair programming.  It does, however have positive effects, but I don't buy into the eXtreme Agile approach to the whole pairing with one keyboard and one mouse.  Working together, communicating, and effectively integrating code into the main continuous integration serves just fine.  But, I can't really knock it either as I haven't truly tried hard core pair programming.

He goes on to slander design patterns, TDD, UML, and a few other things that most developers are fully aware is very helpful in software development.  Not sure why he hates so much of successful software.  I ponder, what does he like about successful software?

In Daniel's defense, he does have some valid topics, just the article is angry and seems to be centered incorrectly and lash out in ways that don't do the points he makes justice.  He does point to a very effective proven use of Agile methodologies, on a massive scale, for a company that turns a little it of profit now.  Google - so read the WHOLE ARTICLE of Stevey's (this guy works for Google and I've read his material several times) and don't be so frustrated with Agile, there ARE good and bad versions.  But I digress, on to the next.

Naming Conventions:  Are They Really That Important?

I stumbled on this entry, I don't know when, but somehow it reflected several conversations I've had recently.  I like naming conventions, when enforced by ReSharper, Intellisense, or something of that sort.  But to remember all, and then have new developers go about learning them all in today's development environment is...

...well...

...an utter waste of time.  Naming conventions are in no way important like they used to be.

Source Control is not a feature you can postpone to vNext

I just thought as I have many times, Microsoft doesn't seem to get source control.  Ayende @ Rahien (the awesome software simian) goes on a slight rant about the absurdity of their solutions so far.

Why are the Microsoft Office file formats so complicated? (And some workarounds)

It also seems that with my new effort to build awesome Office interfaces, tools, and solid back end architecture for these systems I've found the insanity of Microsoft's Office file formats.  I never realized, but Joel Spolsky, who I've read a lot of lately, worked on the Excel team at one point.  He points out that there a more than a few pages (349 in one PDF) of documentation solely about the file formats of Office.

A Field Guide to Developers

Joel also did a great write up in the year 2006 about how to get a keep top developers happy and productive.  Things like private offices, cool toys, a good physical work space, and one of the most important topics being the social existence of programmers.  It points out the importance of having great colleagues, independence and autonomy, how they're treated, and that there are no politics of the feeling type.  As he states in the entry, "And this is the kind of environment you have to create to attract programmers. When a programmer complains about “politics,” they mean—very precisely—any situation in which personal considerations outweigh technical considerations. Nothing is more infuriating than when a developer is told to use a certain programming language, not the best one for the task at hand, because the boss likes it. Nothing is more maddening than when people are promoted because of their ability to network rather than being promoted strictly on merit. Nothing is more aggravating to a developer than being forced to do something that is technically inferior because someone higher than them in the organization, or someone better-connected, insists on it.".  This, no politics philosophy, is absolutely 100% true!

Of only it was more common!

Top Five (Wrong) Reasons you Don't Have Testers

...and an article on why a company, that intends to build good and reliable software should always have software testers.  Again, by Joel Spolsky.

I also dug up another very interesting experiment that the company 37signals undertook.  4 day work weeks.

Workplace Experiments

Amazingly, they got the same out of a 4 day work week as a 5 day work week.  Oddly enough people work much more diligently and effectively when they're able to get a solid weekend of separation from work.  This obviously wouldn't work with every occupation, like for instance McDonalds would probably get minimal amount of use out of 4 day work weeks, but software developers are not McDonalds employees.

Another great article in a similar vein is Increasing sustainable pace.  One of the comments is a classic combination of my railroading interests and software development, "Working more hours may not be the best solution. In the long run, working smarter always wins over working for longer periods. John Henry was the best rail spike driver in his day, and he died proving it. Power tools today let anyone work faster than John ever did.".  I firmly believe that smart workers will produce far more than a bunch of overworked code monkeys.  In my experience this has also been proven time and time again.  This blog entry led me to a few searches, which then led me to some other articles.

That has been my reading for the day.  It's been a ton but I just got on a blog reading kick and figured I'd write up what I've read as of late.

Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

Posted by: Adron
Posted on: 8/18/2008 at 9:03 PM
Categories: Design Patterns | Rants | Discussion Points or Ideas | Agile, Theory, and Process Stuff
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed

Setting Sail with the Smuggling Pattern :: Part 1

Maybe because I'm often entertained by the geekiest of things, I've started usage of the Smuggling Pattern named after a fellow software architect Matt Davis's terminology of smuggling data into and out of Excel.  It is something that I've lately needed to do on a regular basis, get data into and out of Excel without Excel, or at least the evil COM Interop Model, really knowing what is going on.  In other words, I want total programmatic control over data so I can trust it versus Excel mangling my poor data up.  This is what the Smuggling Pattern is going to do for me, and it is doing it well.

Now you might think, "WTF!"  But just to clarify, the Smuggling Pattern is really the Builder Pattern that we architects know and love with a cooler more fun more pirate like name!  So with that, here's some of that code, and the TDD process I undertook to get this sucker built to set sail.

First I created a C# Class Project and created the following file folder structure and files.

Once I completed that I jumped right into testing.  Of course with ReSharper it makes life so much friggin simpler, in addition to TDD making life simpler.  I do still admit, that if you're not practiced at TDD, then doing tests in whatever order you are comfortable with still keeps you at peak performance.  But eventually you should work toward TDD and toss in a great tool like ReSharper.  The difference in speed and quality is almost like getting 2 of the 3 engineering axioms instead of just 1 (i.e. speed, quality, & cost).

The first tests I started on where the individual builder tests, or in my case the Smuggler Object.  I always create a check for null just to make sure that I named the file correctly, kind of a double check, and it gives me the attribute, category, and other parts that I can copy to the other tests easily.

using Production.Smugglers.Builders;
using NUnit.Framework;

namespace Production.Tests
{
    [TestFixture]
    public class SmugglerTests
    {
        [Test, Category("Builder Tests")]
        public void TestsSmugglerIsNotNull()
        {
            var smuggler = new Smuggler();
            Assert.IsNotNull(smuggler);
        }
    }
}

This test I ran and it proved that the smuggler does instantiate appropriately, albeit not doing a single useful thing yet.  Next test...

[Test, Category("Builder Tests")]
public void TestsSmugglerOrderCompletionResult()
{
    var smuggler = new Smuggler();
    smuggler.CompleteOrder();
    SmuggledClassForTest smuggled = smuggler.GetSmuggledSwag();
    Assert.AreEqual(smuggled.StuffTestValue, Resources.ValueForTestingSmugglerSmuggled);
}

Now this test, if you view the picture, didn't color code so well.  ReSharper points out that the instantiated class smuggler doesn't have a CompleteOrder or GetSmuggledSwag method.

image 2

Move the curser (and new character marker over the red method) and hit Alt+Enter (IntelliJ Keymapping) and select the Create method 'Smuggler.CompleteOrder'.

Now we have no more red, and the class Smuggler now has the method CompleteOrder.  The same code that is displayed in the image is directly below it so it can be copied and pasted easily.  Also note, I went ahead and added the IBuilder interface to the class since it will need to meet the contractual obligations of the builder interface.

namespace Production.Smugglers.Builders
{
    public class Smuggler : IBuilder
    {
        public void CompleteOrder()
        {
            throw new NotImplementedException();
        }
    }
}

I then wrote the following additional test, below is the additional test and the ones I already completed in total.

[TestFixture]
public class SmugglerTests
{
    [Test, Category("Builder Tests")]
    public void TestsSmugglerIsNotNull()
    {
        var smuggler = new Smuggler();
        Assert.IsNotNull(smuggler);
    }

    [Test, Category("Builder Tests")]
    public void TestsSmugglerSmuggledIsNotNull()
    {
        Smuggler smuggler = new Smuggler();
        smuggler.CompleteOrder();
        SmuggledClassForTest smuggled = smuggler.GetSmuggledSwag();
        Assert.IsNotNull(smuggled);
    }

    [Test, Category("Builder Tests")]
    public void TestsSmugglerOrderCompletionResult()
    {
        Smuggler smuggler = new Smuggler();
        smuggler.CompleteOrder();
        SmuggledClassForTest smuggled = smuggler.GetSmuggledSwag();
        Assert.AreEqual(smuggled.StuffTestValue, Properties.Resources.ValueForTestingSmugglerSmuggled);
    }
}

After that I went ahead and fleshed out the following classes to get the smuggler tests to green light.  First I stuck a resource variable in for my "test variable value".  One could do this another million ways but I figured this quick n' easy.

This resource file is located in the Properties of the project.

Find the resources by clicking the tab...

Then finished the interface (for now of course).

namespace Production.Smugglers.Builders
{
    public interface IBuilder
    {
        void CompleteOrder();
        SmuggledClassForTest GetSmuggledSwag();
    }
}

Then finished the Smuggler Class (i.e. Builder).

namespace Production.Smugglers.Builders
{
    public class Smuggler : IBuilder
    {
        private readonly SmuggledClassForTest smuggledClassForTest = new SmuggledClassForTest();

        public void CompleteOrder()
        {
            smuggledClassForTest.StuffTestValue = Resources.ValueForTestingSmugglerSmuggled;
        }

        public SmuggledClassForTest GetSmuggledSwag()
        {
            return smuggledClassForTest;
        }
    }
}

Then the little SmuggledClassForTests class, which is basically the product of the builder.

namespace Production.Smugglers.Smuggled
{
    public class SmuggledClassForTest
    {
        public string StuffTestValue { get; set; }
    }
}

Note the namespaces, I make a point to follow the standard of maintaining folder and namespace integrity.  I promise keeping the integrity makes getting to and finding code way faster than getting those things out of sync.

I then ran the tests again...

...green lights!

kick it on DotNetKicks.com

Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

Enterprise Library by Part

Sometimes I want to use the enterprise library, but just not all 200+ MB of the thing.  So I go about piecing together the pieces I want, by project, into the main solution.  This can be troublesome as all the pieces don't just drop in simply.

First Bump in the Road :: GlobalAssemblyInfo.cs

The first problem I've run into this time around was the GlobalAssemblyInfo.cs file wasn't available.  After fiddling around a minute I realized this needed to be added to the solution at a global level.  The way to do this is to right click and add existing file to the actual Solution.  Unfortunately I had statically added a copy of this file to my projects I was adding and simply re-added those projects with the appropriate file mapping again.  Note to self, "If adding individual blocks from the enterprise library make sure to add the GlobalAssemblyInfo.cs file first and then the individual blocks".

Second Bump ::  The "Common" Project

The common project is used in pretty much every single project in the enterprise library solution.  For some reason when I added all my projects, the common reference went away in numerous projects.  So this was a case of just adding the reference back to several of the projects.  Maybe I shouldn't add them this way, but regardless, it gets the job done.

So far I'm now up and running, but just wanted to present a searchable note about these bumps I've hit.  Once these things are resolved the library blocks are flawless.

Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

Posted by: Adron
Posted on: 7/20/2008 at 12:48 PM
Categories: My Projects | How-To, Samples, and Such | Design Patterns | Transit Engine
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed

Stand Up, SCRUM, or Some Other Name - Pt. 3 - Super Day

Yesterday was rough going, mainly just because there was so many topics to cover.  Today I hope to nail a ton more functional code segments, requirements, and get some unit tests in my work.

Yesterday

  • Finished clarification of the task list and working to find the priorities for this iteration.
  • Met to clarify design goals and functional requirements for new features.

Today

  • Core focus will be on functional requirements and getting the respective unit tests, preferably via TDD for the interface based controller and presenter code.  I'll be happy with test after for the UI elements since they're all COM Interop.  i.e.,  today is code day.

Tomorrow

  • Continue focus on functional specifications and respective tests.

Road Blocks & Risks

  • Continued - Time constraints have all but eliminated efforts to research or implement a proof around the PIAB, limiting our exception handling, logging, and other cross cutting concerns we need to have in place.
  • Continued - The IoC and DI architecture points have not been clarified.  The more UI work we do without these pieces in place the more tightly coupled, untested, and unworkable refactor efforts will be in the future.
  • Continued - Significant risks include; lack of significant code coverage from unit tests, unclear way for testing the upper layers of Excel/UI functionality.
Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

TDD, Architecture and Testing Code in Isolation :: Part 2

Navigate back to Part 1 of this series of entries.

The next steps I undertook are some of my various clean ups.  Create respective test classes for each of the pieces of the database that will have respective entities, methods, or objects to test.

I like to keep code clean, easy to read, and were it needs to be.  I hate code bloat, misplaced code sections, and above all I can't stand arbitrary pieces of using statements, random misuses of methods (such as ToString()) and other such things.  Luckily for me JetBrains Software created ReSharper.  So before going to far headlong into the tests I always run a few "Cleanup Code..." functions on my newly created files.

I usually just leave the cleanup code settings on full and click run.

The code already, with just the crude skeleton goes from this...

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Linq;
   4:  using System.Text;
   5:  using MbUnit.Framework;
   6:   
   7:  namespace DataAccessLayer.Unit.mbUnitTests
   8:  {
   9:      [TestFixture]
  10:      public class Users
  11:      {
  12:          [Test]
  13:          public void Test()
  14:          {
  15:              //
  16:              // TODO: Add test logic here
  17:              //
  18:          }
  19:      }
  20:  }

...to this...

using MbUnit.Framework;
   1:  namespace DataAccessLayer.Unit.mbUnitTests
   2:  {
   3:      [TestFixture]
   4:      public class Users
   5:      {
   6:          [Test]
   7:          public void Test()
   8:          {
   9:              //
  10:              // TODO: Add test logic here
  11:              //
  12:          }
  13:      }
  14:  }

Reason number eight gazillion that ReSharper RULEZ!  I almost forgot too, I need to have my Rhino Mock Library References.  If you don't have the Rhino Mock Library you can download it.  Generally I just put a folder in the solution, stick the DLL in it, and then browse to make a reference.

Now that we have clean test fixtures I'm going to run some green lights.  Green light tests that is, which is not in true TDD fashion.  The reason though is because this layer will be tests against a generated layer (the LINQ to SQL).  The generated layer doesn't really require a red light green light process, so I'm just going to write the bare minimum of green light tests against the layer.  Then when we go to the next layer, which WILL need to be red light green light I'll already have this layer tested appropriately. 

Before going any further and I forget another reference I'm grabbing the DAL Layer assembly.

Now that I've managed to not forget the references, which I've been working on doing, and I have the appropriate items in place.  The next step NOW is to get those green lights.  Off to the reckless abandon of TDD.  The first tests I write are simple, no frills, just to get and test the TsrUser and TmtUser Entities.

   1:  using System;
   2:  using MbUnit.Framework;
   3:  using Rhino.Mocks;
   4:   
   5:  namespace DataAccessLayer.Unit.mbUnitTests
   6:  {
   7:      [TestFixture]
   8:      public class Users
   9:      {
  10:          [Test]
  11:          public void TestTsrUserEntity()
  12:          {
  13:              Guid newUserId = Guid.NewGuid();
  14:              TsrUser tsrUser = MockRepository.GenerateStub<TsrUser>();
  15:              tsrUser.UserId = newUserId;
  16:              Assert.AreEqual(tsrUser.UserId, newUserId);
  17:          }
  18:   
  19:          [Test]
  20:          public void TestTmtUserEntity()
  21:          {
  22:              Guid newUserId = Guid.NewGuid();
  23:              TmtUser tmtUser = MockRepository.GenerateStub<TmtUser>();
  24:              tmtUser.UserId = newUserId;
  25:              Assert.AreEqual(tmtUser.UserId, newUserId);
  26:          }
  27:      }
  28:  }

Next, hit up the User Lists derived from the views.  This is where the mocking gets truly interesting.  The above tests, while they help, really don't do much of anything.  Next what I need is a way to mock out the DataContext Class, which doesn't have an interface.  This is a slight issue, but just requires a little manual coding to get a good mock going.  Since I knew I'd be using this for multiple testing projects I've created a completely new project to reference.  Since I'm not always creative I'm naming it "UnitTest" and then will name the files respectively.

I also found a bit of code specifically for mocking the DataContext over at Andrew Tokeley's Blog on Mocking LINQ to SQL DataContext.  I've snagged it and have altered it for the particular project.  Make sure to give dibs to Andrew!  Below are the modified interfaces and the respective classes.  Make sure to add a reference to the DAL for the project too.  Then add the files listed below in the screen shot.

IDataContextWrapper Interface

   1:  using System;
   2:  using System.Collections.Generic;
   3:   
   4:  namespace UnitTest.Interfaces
   5:  {
   6:      public interface IDataContextWrapper : IDisposable
   7:      {
   8:          List<T> Table<T>() where T : class;
   9:          void DeleteAllOnSubmit<T>(IEnumerable<T> entities) where T : class;
  10:          void DeleteOnSubmit<T>(T entity) where T : class;
  11:          void InsertOnSubmit<T>(T entity) where T : class;
  12:          void SubmitChanges();
  13:      }
  14:  }

IMockDatabase Interface

   1:  using System;
   2:  using System.Collections;
   3:  using System.Collections.Generic;
   4:   
   5:  namespace UnitTest.Interfaces
   6:  {
   7:      public interface IMockDatabase
   8:      {
   9:          Dictionary<Type, IList> Tables { get; set; }
  10:      }
  11:  }

MockDatabase Class

   1:  using System;
   2:  using System.Collections;
   3:  using System.Collections.Generic;
   4:   
   5:  namespace UnitTest.Mocks
   6:  {
   7:      /// <summary>
   8:      /// Abstract Template class that represents our in memory database. We can create different implementations of this class that contain different
   9:      /// tables and data.
  10:      /// </summary>
  11:      public abstract class MockDatabase
  12:      {
  13:          protected MockDatabase()
  14:          {
  15:              InitializeDataBase();
  16:          }
  17:   
  18:          public Dictionary<Type, IList> Tables { get; set; }
  19:   
  20:          private void InitializeDataBase()
  21:          {
  22:              Tables = new Dictionary<Type, IList>();
  23:              CreateTables();
  24:              PopulateTables();
  25:          }
  26:   
  27:          protected abstract void CreateTables();
  28:          protected abstract void PopulateTables();
  29:   
  30:          protected void AddTable<T>()
  31:          {
  32:              var table = new List<T>();
  33:              Tables.Add(typeof (T), table);
  34:          }
  35:   
  36:          protected List<T> GetTable<T>()
  37:          {
  38:              return (List<T>) Tables[typeof (T)];
  39:          }
  40:      }
  41:  }

MockDatabaseContextWrapper Wrapper Class

   1:  using System.Collections.Generic;
   2:  using UnitTest.Interfaces;
   3:   
   4:  namespace UnitTest.Mocks
   5:  {
   6:      /// <summary>
   7:      /// A linq to sql wrapper class. This is a mock implementation of IDataContextWrapper
   8:      /// that works directly with an in memory version of a database 
   9:      /// </summary>
  10:      public class MockDataContextWrapper : IDataContextWrapper
  11:      {
  12:          private readonly MockDatabase _mockDatabase;
  13:   
  14:          public MockDataContextWrapper(MockDatabase database)
  15:          {
  16:              _mockDatabase = database;
  17:          }
  18:   
  19:          #region IDataContextWrapper Members
  20:   
  21:          public List<T> Table<T>() where T : class
  22:          {
  23:              return (List<T>) _mockDatabase.Tables[typeof (T)];
  24:          }
  25:   
  26:          public void DeleteAllOnSubmit<T>(IEnumerable<T> entities) where T : class
  27:          {
  28:              foreach (T entity in entities)
  29:              {
  30:                  Table<T>().Remove(entity);
  31:              }
  32:          }
  33:   
  34:          public void DeleteOnSubmit<T>(T entity) where T : class
  35:          {
  36:              Table<T>().Remove(entity);
  37:          }
  38:   
  39:          public void InsertOnSubmit<T>(T entity) where T : class
  40:          {
  41:              Table<T>().Add(entity);
  42:          }
  43:   
  44:          public void SubmitChanges()
  45:          {
  46:          }
  47:   
  48:          public void Dispose()
  49:          {
  50:          }
  51:   
  52:          #endregion
  53:      }
  54:  }

DataContextWrapper Wrapper Class

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Data.Linq;
   4:  using System.Linq;
   5:  using UnitTest.Interfaces;
   6:   
   7:  namespace UnitTest
   8:  {
   9:      /// <summary>
  10:      /// A linq to sql wrapper class for the Datacontext object. This is the real implementation of IDataContextWrapper
  11:      /// that works directly with a database 
  12:      /// </summary>
  13:      /// <typeparam name="T"></typeparam>
  14:      public class DataContextWrapper<T> : IDataContextWrapper where T : DataContext, new()
  15:      {
  16:          private readonly T db;
  17:          private bool _disposed;
  18:   
  19:          public DataContextWrapper()
  20:          {
  21:              Type t = typeof (T);
  22:              db = (T) Activator.CreateInstance(t);
  23:          }
  24:   
  25:          public DataContextWrapper(string connectionString)
  26:          {
  27:              Type t = typeof (T);
  28:              db = (T) Activator.CreateInstance(t, connectionString);
  29:          }
  30:   
  31:          #region IDataContextWrapper Members
  32:   
  33:          public List<TableName> Table<TableName>() where TableName : class
  34:          {
  35:              var table = (Table<TableName>) db.GetTable(typeof (TableName));
  36:   
  37:              return table.ToList();
  38:          }
  39:   
  40:          public void DeleteAllOnSubmit<Entity>(IEnumerable<Entity> entities) where Entity : class
  41:          {
  42:              db.GetTable(typeof (Entity)).DeleteAllOnSubmit(entities);
  43:          }
  44:   
  45:          public void DeleteOnSubmit<Entity>(Entity entity) where Entity : class
  46:          {
  47:              db.GetTable(typeof (Entity)).DeleteOnSubmit(entity);
  48:          }
  49:   
  50:          public void InsertOnSubmit<Entity>(Entity entity) where Entity : class
  51:          {
  52:              db.GetTable(typeof (Entity)).InsertOnSubmit(entity);
  53:          }
  54:   
  55:          public void SubmitChanges()
  56:          {
  57:              db.SubmitChanges();
  58:          }
  59:   
  60:          public void Dispose()
  61:          {
  62:              Dispose(true);
  63:              GC.SuppressFinalize(this);
  64:          }
  65:   
  66:          #endregion
  67:   
  68:          private void Dispose(bool disposing)
  69:          {
  70:              if (_disposed)
  71:                  return;
  72:   
  73:              if (disposing)
  74:              {
  75:                  db.Dispose();
  76:              }
  77:   
  78:              _disposed = true;
  79:          }
  80:      }
  81:  }

ExampleMockDatabase Class

   1:  using System;
   2:  using DataAccessLayer;
   3:   
   4:  namespace UnitTest.Mocks
   5:  {
   6:      /// <summary>
   7:      /// This is an actual implementation of our database containing specific tables and data.
   8:      /// </summary>
   9:      public class ExampleMockDatabase : MockDatabase
  10:      {
  11:          protected override void CreateTables()
  12:          {
  13:              AddTable<TsrUserListing>();
  14:          }
  15:   
  16:          protected override void PopulateTables()
  17:          {
  18:              var testTsrUserListing1 =
  19:                  new TsrUserListing
  20:                      {
  21:                          UserId = Guid.NewGuid(),
  22:                          UserName = "Test User",
  23:                          LoweredUserName = "test user",
  24:                          IsAnonymous = false,
  25:                          LastActivityDate = DateTime.Now.AddDays(-4),
  26:                          MobileAlias = "test user"
  27:                      };
  28:              var testTsrUserListing2 =
  29:                  new TsrUserListing
  30:                      {
  31:                          UserId = Guid.NewGuid(),
  32:                          UserName = "User Test",
  33:                          LoweredUserName = "user test",
  34:                          IsAnonymous = true,
  35:                          LastActivityDate = DateTime.Now.AddMinutes(-23),
  36:                          MobileAlias = "user test"
  37:                      };
  38:              var testTsrUserListing3 =
  39:                  new TsrUserListing
  40:                      {
  41:                          UserId = Guid.NewGuid(),
  42:                          UserName = "John Doe",
  43:                          LoweredUserName = "john doe",
  44:                          IsAnonymous = true,
  45:                          LastActivityDate = DateTime.Now.AddHours(-22),
  46:                          MobileAlias = "john doe"
  47:                      };
  48:   
  49:              GetTable<TsrUserListing>().Add(testTsrUserListing1);
  50:              GetTable<TsrUserListing>().Add(testTsrUserListing2);
  51:              GetTable<TsrUserListing>().Add(testTsrUserListing3);
  52:          }
  53:      }
  54:  }

CustomerController Class

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Linq;
   4:  using DataAccessLayer;
   5:  using UnitTest.Interfaces;
   6:   
   7:  namespace UnitTest.Controller
   8:  {
   9:      public class TsrUserListingController
  10:      {
  11:          public IDataContextWrapper DataContext { get; set; }
  12:   
  13:          public IEnumerable<TsrUserListing> GetTsrUserListing(DateTime lastActivityDate)
  14:          {
  15:              IEnumerable<TsrUserListing> tsrUserListings = from tsrUserListing in DataContext.Table<TsrUserListing>()
  16:                                                            where tsrUserListing.LastActivityDate >= lastActivityDate
  17:                                                            select tsrUserListing;
  18:              return tsrUserListings;
  19:          }
  20:   
  21:          public IEnumerable<TsrUserListing> GetTsrUserListing()
  22:          {
  23:              IEnumerable<TsrUserListing> tsrUserListings = from tsrUserListing in DataContext.Table<TsrUserListing>()
  24:                                                            select tsrUserListing;
  25:              return tsrUserListings;
  26:          }
  27:      }
  28:  }

Now that we have the database fake/mock - whatever one wants to call it - we're ready for some tests.  So stay tuned, I'll have it up tomorrow.

kick it on DotNetKicks.com

Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

TDD, Architecture and Testing Code in Isolation :: Part 1

Just a few examples here.  First we'll setup the basic architecture from the lowest common denominator - the database - bounce up to the first layer, and test in isolation and test in integration.  These examples are simply the easiest ways to test the basic data and data access layer using Stubs, Mocks, and general unit test practices.

First off here are the pieces of the architecture that you'll NEED regardless.  No real coding in this entry (except for the T-SQL), just setting up the solution to put the tests in and have the initial database architecture setup.  The part 2 entry will have more on the testing, and part 3 will have actual functional code tests and implementations - after all, I'm working this up in a test driven development mentality - "mostly".

The Beginning

Setup the following projects within a new VS.NET solution.  I broke out the projects as you see below. (Click to see the larger image)

I like to keep my unit and integration tests separate so that they're easy to include or exclude from a continuous integration build.  Because really, the integration tests aren't generally going to work in a regular build.  Unless of course you've scripted out some serious steps such as "Create a Setup MSI" and "install the software and run" etc.  Which in my mind, just seems a bit out of line with the build process.  Not to even mention extremely time consuming.  I however think that a good solid and separate CI build of the respective setup project should be kept in a different solution that when it does alter can have a build process and either an automated integration test run or have the integration tests manually run.  Either way, they get run, but in my humble opinion shouldn't be run during the regular CI build of the working solution.

If you don't have mbUnit installed, go grab a copy over at www.mbunit.com.

The next thing I did is setup the databases.  I have some odd requirements for the particular database schema and segmentation of various databases for the application components.  The Grand Central Database is used for the security, membership, roles, etc, the other databases are used for each of the application "components".  Each of these "component" databases I'll refer to as either "component" or "tsr" or "tmt" respectively.  These "component" databases I've setup for use once a user is authenticated.  So each of these databases has a Users Table that has a UserId Column that will be used for relating the respective user that has access to the particular component.

First I setup the tables:

tmtUsers

   1:  /****** Object:  Table [dbo].[tmtUsers]    Script Date: 07/06/2008 01:27:07 ******/
   2:  IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[tmtUsers]') AND type in (N'U'))
   3:  DROP TABLE [dbo].[tmtUsers]
   4:  GO
   5:  /****** Object:  Table [dbo].[tmtUsers]    Script Date: 07/06/2008 01:27:07 ******/
   6:  SET ANSI_NULLS ON
   7:  GO
   8:  SET QUOTED_IDENTIFIER ON
   9:  GO
  10:  IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[tmtUsers]') AND type in (N'U'))
  11:  BEGIN
  12:  CREATE TABLE [dbo].[tmtUsers](
  13:      [UserId] [uniqueidentifier] NOT NULL,
  14:   CONSTRAINT [PK_tmtUsers] PRIMARY KEY CLUSTERED 
  15:  (
  16:      [UserId] ASC
  17:  )WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
  18:  ) ON [PRIMARY]
  19:  END
  20:  GO
 

tsrUsers

 
   1:  /****** Object:  Table [dbo].[tsrUsers]    Script Date: 07/06/2008 01:50:30 ******/
   2:  IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[tsrUsers]') AND type in (N'U'))
   3:  DROP TABLE [dbo].[tsrUsers]
   4:  GO
   5:  /****** Object:  Table [dbo].[tsrUsers]    Script Date: 07/06/2008 01:50:31 ******/
   6:  SET ANSI_NULLS ON
   7:  GO
   8:  SET QUOTED_IDENTIFIER ON
   9:  GO
  10:  IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[tsrUsers]') AND type in (N'U'))
  11:  BEGIN
  12:  CREATE TABLE [dbo].[tsrUsers](
  13:      [UserId] [uniqueidentifier] NOT NULL,
  14:   CONSTRAINT [PK_tsrUsers] PRIMARY KEY CLUSTERED 
  15:  (
  16:      [UserId] ASC
  17:  )WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
  18:  ) ON [PRIMARY]
  19:  END
  20:  GO


After creating the tables I went ahead and created a view that would link the UserId do the Grand Central Database and a respective stored procedure too.  I created these by simply dragging the tables onto the designer for creating the view from each of the respective databases. (Click to see the larger image)

The SQL for the views is below.  The TSR and TMT Databases both use basically the same exact SQL.

tmtUserListing

   1:  SELECT     dbo.tmtUsers.UserId, GrandCentral.dbo.aspnet_Users.UserName
   2:  FROM         dbo.tmtUsers LEFT OUTER JOIN
   3:                        GrandCentral.dbo.aspnet_Users ON dbo.tmtUsers.UserId = GrandCentral.dbo.aspnet_Users.UserId

tsrUserListing

   1:  SELECT     dbo.tsrUsers.UserId, GrandCentral.dbo.aspnet_Users.UserName, GrandCentral.dbo.aspnet_Users.LoweredUserName, GrandCentral.dbo.aspnet_Users.MobileAlias, 
   2:                        GrandCentral.dbo.aspnet_Users.IsAnonymous, GrandCentral.dbo.aspnet_Users.LastActivityDate
   3:  FROM         dbo.tsrUsers INNER JOIN
   4:                        GrandCentral.dbo.aspnet_Users ON dbo.tsrUsers.UserId = GrandCentral.dbo.aspnet_Users.UserId
 

and last but not least the two stored procedures.

tmtUserListingSelect

 
   1:  /****** Object:  StoredProcedure [dbo].[tmtUserListingSelect]    Script Date: 07/06/2008 01:36:33 ******/
   2:  IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[tmtUserListingSelect]') AND type in (N'P', N'PC'))
   3:  DROP PROCEDURE [dbo].[tmtUserListingSelect]
   4:  GO
   5:  /****** Object:  StoredProcedure [dbo].[tmtUserListingSelect]    Script Date: 07/06/2008 01:36:33 ******/
   6:  SET ANSI_NULLS ON
   7:  GO
   8:  SET QUOTED_IDENTIFIER ON
   9:  GO
  10:  IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[tmtUserListingSelect]') AND type in (N'P', N'PC'))
  11:  BEGIN
  12:  EXEC dbo.sp_executesql @statement = N'CREATE PROCEDURE dbo.tmtUserListingSelect
  13:      
  14:  AS
  15:      SELECT     dbo.tmtUsers.UserId, GrandCentral.dbo.aspnet_Users.UserName
  16:  FROM         dbo.tmtUsers LEFT OUTER JOIN
  17:                        GrandCentral.dbo.aspnet_Users ON dbo.tmtUsers.UserId = GrandCentral.dbo.aspnet_Users.UserId
  18:                        
  19:      RETURN
  20:  ' 
  21:  END
  22:  GO

tsrUserListingSelect

 
   1:  /****** Object:  StoredProcedure [dbo].[tsrUserListingSelect]    Script Date: 07/06/2008 01:50:38 ******/
   2:  IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[tsrUserListingSelect]') AND type in (N'P', N'PC'))
   3:  DROP PROCEDURE [dbo].[tsrUserListingSelect]
   4:  GO
   5:  /****** Object:  StoredProcedure [dbo].[tsrUserListingSelect]    Script Date: 07/06/2008 01:50:38 ******/
   6:  SET ANSI_NULLS ON
   7:  GO
   8:  SET QUOTED_IDENTIFIER ON
   9:  GO
  10:  IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[tsrUserListingSelect]') AND type in (N'P', N'PC'))
  11:  BEGIN
  12:  EXEC dbo.sp_executesql @statement = N'CREATE PROCEDURE dbo.tsrUserListingSelect
  13:      
  14:  AS
  15:  SELECT     dbo.tsrUsers.UserId, GrandCentral.dbo.aspnet_Users.UserName, GrandCentral.dbo.aspnet_Users.LoweredUserName, GrandCentral.dbo.aspnet_Users.MobileAlias, 
  16:                        GrandCentral.dbo.aspnet_Users.IsAnonymous, GrandCentral.dbo.aspnet_Users.LastActivityDate
  17:  FROM         dbo.tsrUsers INNER JOIN
  18:                        GrandCentral.dbo.aspnet_Users ON dbo.tsrUsers.UserId = GrandCentral.dbo.aspnet_Users.UserId
  19:      RETURN
  20:  ' 
  21:  END
  22:  GO
 

Now your server explorer should look like the image below.  (Click to see the larger image)

My database projects appear as below (all checked in). (Click to see the larger image)

Next we're going to setup the Data Access Layer (DAL).  Yes, this is somewhat crude, but really, we're only trying to get the basic objects and setup the business entities.  I've used SQL to LINQ for the DAL.  I dragged the three database objects from each "component" database over to the respective *.dbml files.

After that add the testing files to the various mbUnit test projects that have been created.  I create one for each of the objects that will be tested from the database, mainly based on the idea that they will generate entities or objects of some type, and this will keep them fairly organized.  A little bit more about this in part 2.

I'll have the next part up soon.  Until then this will get one setup for the initial tests and core data access layer.

kick it on DotNetKicks.com

Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

DI, IoC, and Loose Coupling

Recently a situation were I needed to pull out the dependency injection and inversion of control skills came up again.  The need for loose coupling in so many projects is vital to these patterns.  I was going to start writing an article, but then realized there are some really amazing ones out there.  They point out the reason for Separation of Concerns (SoC), Loose Coupling of UI Components, Controller Independence (free from proprietary UI code).  With that said, here's some links;

Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

Posted by: Adron
Posted on: 6/25/2008 at 7:28 AM
Categories: Design Patterns
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed

.NET Middle Tier Work

A recent topic that has come up in discussion is pieces of middle tier patterns and libraries.  I decided I'd enumerate some of the pieces I've used in middle tier work throughout my career, then I thought, "why?"  Instead, I'm going to scour the net and grab some current libraries, CTPs, patterns and practices, and whatever else I could find that helps one build a nice solid middle tier to tie together the front and back ends of an application.

The current big players in the middle tier stack for .NET Technologies are the Windows Communication Foundation (WCF)) and the Enterprise Application Blocks.  These two pieces are huge in the maturing of Microsoft middle tier components.  Some of the secondary players at this point, but should in no way be less important are the various factories (Web Service Software Factory, Smart Client Software Factory), CTPs, libraries, and namely one that could be huge in the near future the ASP.NET MVC.  In this article I'm going to discuss some of the points of use and ideas for use of these various pieces of the Microsoft Technology Stack.

Middle Tier WCF (As if it would be an interface or a data storage mechanism?!)  Surprise [:O]

First the WCF, which I've been working with well since the beta, absolutely rocks.  Now some of you hard core WCF grognards might think, "no it doesn't, its slow" or "all it does it force coding to interfaces" and the list of gripes goes on.  But the list of advantages, like it might be faster (toward the bottom), far outweighs the list of disadvantages.  In some cases the speed might be an issue, but probably not, and in some cases the coding to interfaces might be an issue for some, but I really hope it isn't as that is a sign of other significant issues.

The WCF layer is very flexible, being that it is primarily a transport foundation, nothing more.  This opens up the ability for all types of SOA (Services Oriented Architecture) development that isn't locked to a particular transport mechanism.  As has been written and spoken about before, web services does not equate to SOA, SOA is too high level to sync with the underlying transport mechanism.

The Windows Communication Foundation also works great when utilized merely as a transport mechanism with validation, authentication, and other pieces pushed off into use with the Policy Injection Application Block (PIAB).  Also check out the integration of the PIAB with WCF article.  With the validation or other pieces of the middle tier cleanly pushed off into the PIAB we get a better separation of concerns with the obvious increase in maintainable alterations down the road.  The validation layer could change via the way the PIAB access the rules and the WCF layer wouldn't have to change one bit.

Middle Tier Enterprise Application Blocks

Speaking of the PIAB, the latest Enterprise Application Blocks are super sweet.  The new pieces, such as the PIAB, help increase the use of a peripheral components as well as the Enterprise Application Blocks themselves.  In a middle tier environment the business logic and crosscutting concerns make a tangled mess in code.  Maintenance and readability become a serious and expensive concern.  With the PIAB though, the separation of concerns allows a much cleaner break out of the tiers work so that maintenance and readability are exponentially increased, and thus time and costs decrease once implemented.

The crosscutting idea behind the PIAB sits outside of a particular code base and is implemented via a policy.  These policies are acted upon by a pipeline of handlers.  The way these policies are initiated is through mere applied attributes.  This makes the intrusion into other workflow, web services, or other code very minimal but still obvious.  Thus the simplification and separation of concerns into respective code areas causes a decrease in code spaghetti and the resultant nightmares.

So what about the other parts of the current EAB (Enterprise Application Blocks)?  The parts as I remember just off hand are; caching, cryptography, data access, exception handling, logging, security, and validation.  Each of these sections can have huge advantages in building out middle tier operations and assure good separation of concerns within the code base.

Middle Tier - My Comments, Thoughts, and some other Ideas

Generally speaking it is fairly easy, once one knows the proper tools and libraries, to get an architecture setup and ready to go for an enterprise application, the catch comes when actual implementation starts.  Knowing good pattern ideas and libraries only really gets the implementation half baked.  The need to be able to flex and bend the middle tier, and architecture in general is pivotal to success for a project.  With tools like the PIAB, WCF, and other new tools and such that I've mentioned, that flexibility for implementation is available to a project.

Middle Tier - Last Thoughts and Unit Testing

Another big plus, that I almost forgot to mention is the ability to test middle tier components.  This is an awesome part of middle tier code areas such as validation, policy injection, etc.  With the front end or even back end database it is somewhat difficult to test in isolation, in the middle tier however it is vastly simpler to turn out some unit test, either via TDD or test after development.  For testability within enterprise application development it is very important that a middle tier is developed with unit tests.  Sometimes this causes slight changes to middle tier architecture, such as interfaces being utilized where they might not have to increase the ability to mock various objects easier, but for the most part any middle tier is exponentially more testable and offers that much more integrity during deployment.  So with the middle tier always built with testing in mind - it'll work better in the end.

For now, I'm off to write some view code and figure out associated unit tests for that layer, that will then be connecting to my middle tier.  I just wanted to ramble off a few of my thoughts on middle tier development and kind of discuss (yes, sort of like speaking to myself) the latest and greatest in middle tier tools and architecture components.

Smile [:)]

Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

Posted by: Adron
Posted on: 2/8/2008 at 12:54 PM
Categories: Design Patterns | Discussion Points or Ideas
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed

The Oncoming Microsoft ASP.NET MVC - Part 1

The ASP.NET MVC is officially released in the .NET 3.5 Extensions CTP.  This is part one of a multi-part series I'm going to do on getting a basic application up and running with good practice and solid test driven development process.  Enjoy.

First we'll go over a quick review.  What is MVC?  MVC stands for the model view controller pattern.

  • The Model in an MVC pattern are the components of the application that are responsible for maintaining state.
  • The View in this pattern are the parts responsible for displaying the application's user interface.
  • The Controller in this pattern are responsible for handling end user interaction.  Manipulating the model and choosing a view to render for the UI is also part of the responsibilities of the controllers.

In each of the above parts of the pattern, you could have multiple models, views, or controllers depending on the need and setup of the application.

Some of the absolutely kick ass benefits of the MVC pattern include;

  • To me, one of the largest benefits of the MVC pattern is the separation of concerns.  I've ranted and raved about the need for a separation of concerns in some of my other blog entries.  This is fundamental to maintainable, easy to understand, and easy to read code.  Without a clear separation of concerns it is easy to toss out all three; maintainability, understanding, and readability.
  • The new MVC does not use the post back model for server interactions.  What is used is interactions with a controller class which enables the above mentioned separation of concerns and testing.
  • Unit testing and test driven development are easily implemented with the MVC pattern.  Especially so with this particular implementation of the pattern.  All the contracts within the framework are interface declared and easily mock-able.  Unit testing the application can be done without needing to run the controllers within the ASP.NET process making them turbo fast.
  • Dependency injection and inversion of control container models can be integrated and used.  This could include Windsor, Spring.NET, NHibernate, probably even StructureMap or others.

So with all this awesomeness released finally I've set out to build my first ASP.NET MVC How To.  Since I've been needing to rebuild my Transportation Tracker Application I decided that would be my first ASP.NET MVC Candidate for building.  This quick tutorial is going to assume that you have Visual Studio 2008 installed and the .NET 3.5 Extensions CTP mentioned above installed already.  In addition to these things I'm also assuming that we all have Rhino Mocks installed.  If not, get it.

Spring.NET Framework

Go hit up the Spring.net Framework.  Download that and install it, making sure to have Visual Studio 2008 closed.  Once you've got that installed we're ready to go.  Click on the images to see the full size versions.

Creating the Project

First step after all that installation mess is to get a project and the proper test project created!  Just open up Visual Studio 2008 and click on file, then new so the new project dialog appears.  Name the project whatever you'd like to at this point.  Same thing with these images, click em' if you want to see the full size ones.

Once you've created the project with the test project, then create some initial model, view, and controller files that will be the basis of the application.  The first few I created are the interfaces for the model classes; 

Following the interfaces I created the model class files.  At this point I have a Solution Explorer Tree that looks like the image below.

Next create the necessary controllers for the application.  I'm going to break this apart into each separate section of the application that I will have data manipulation.  Each of these sections is basically a separate page with specific flow and data characteristics on each page.

Creating new files and code 03

Note when you are creating the MVC Controller Classes that there is a template item specifically for creating these that cut out some of the work for you, albeit a small amount.  What you get is a few attributes marking the class and initial void that is put in place for you.

public class TripController : Controller
{

    [ControllerAction]
    public void Index()
    {
        //Add code action logic here
    }
}

After adding each of the controllers I've got; LocationController, TransitModeController, TripController, TripPurposeController, TripTypeController, and the HomeController that was already generated by the template for me.  Since I'm trying to maintain my test driven development methodologies these days, I'm going to add a quick few pieces of code so that I can get something building and add some tests.

I added the following tests for the HomeController to assure that the proper items where setup and displaying properly.  Of course since I'm doing TDD, and I have none of this implemented yet, none of the tests will pass.  The following is my test code;

using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MvcApplication.Controllers;

namespace MvcApplicationTest.Controllers
{
    [TestClass]
    public class TransportationControllerTests
    {
        [TestMethod]
        public void HomeControllerSelectsCorrectViewTripType()
        {
            HomeControllerDouble controller = new HomeControllerDouble();
            controller.TripTypes();
            Assert.AreEqual("TripType", controller.SelectedView);
        }

        [TestMethod]
        public void HomeControllerSelectsCorrectViewTrip()
        {
            HomeControllerDouble controller = new HomeControllerDouble();
            controller.Trips();
            Assert.AreEqual("Trip", controller.SelectedView);
        }

        [TestMethod]
        public void HomeControllerSelectsCorrectViewTripPurpose()
        {
            HomeControllerDouble controller = new HomeControllerDouble();
            controller.TripPurpose();
            Assert.AreEqual("TripPurpose", controller.SelectedView);
        }

        [TestMethod]
        public void HomeControllerSelectsCorrectViewTransitModes()
        {
            HomeControllerDouble controller = new HomeControllerDouble();
            controller.TransitModes();
            Assert.AreEqual("TransitMode", controller.SelectedView);
        }

        [TestMethod]
        public void HomeControllerSelectsCorrectViewHome()
        {
            HomeControllerDouble controller = new HomeControllerDouble();
            controller.Index();
            Assert.AreEqual("Index", controller.SelectedView);
        }

        [TestMethod]
        public void HomeControllerSelectsCorrectViewAbout()
        {
            HomeControllerDouble controller = new HomeControllerDouble();
            controller.About();
            Assert.AreEqual("About", controller.SelectedView);
        }

        [TestMethod]
        public void HomeControllerSelectsCorrectLocation()
        {
            HomeControllerDouble controller = new HomeControllerDouble();
            controller.Location();
            Assert.AreEqual("Location", controller.SelectedView);
        }

        private class HomeControllerDouble : HomeController
        {
            public HomeControllerDouble()
                : base()
            { }

            public string SelectedView { get; private set; }
            public object RenderedViewData { get; private set; }

            protected override void RenderView(string viewName
              , string masterName
              , object viewData)
            {
                this.SelectedView = viewName;
                this.RenderedViewData = viewData;
            }
        }
    }
}

Now that there are some tests to verify each of the different areas I'm going to move on to wiring these sections up.  After editing the HomeController.cs the code looks like the following;

using System;
using System.Web;
using System.Web.Mvc;

namespace MvcApplication.Controllers
{
    public class HomeController : Controller
    {
        [ControllerAction]
        public void Index()
        {
            RenderView("Index");
        }

        [ControllerAction]
        public void About()
        {
            RenderView("About");
        }

        [ControllerAction]
        public void TransitModes()
        {
            RenderView("TransitMode");
        }

        [ControllerAction]
        public void Trips()
        {
            RenderView("Trip");
        }

        [ControllerAction]
        public void TripPurpose()
        {
            RenderView("TripPurpose");
        }

        [ControllerAction]
        public void TripTypes()
        {
            RenderView("TripType");
        }

        [ControllerAction]
        public void Location()
        {
            RenderView("Location");
        }
    }
}

Now we have a compiling, unit test executing, running application.  Albeit there isn't a whole bunch of use to the thing yet.  Next thing we'll do is setup some testing that will utilize the Rhino Mocks framework.  Keep in mind while working through this that we're doing test driven development.  We get a test written, it tosses a red light, we then fix it so that we get a green light.  On and on goes the process.  The first test I'm going to write is for the TransitMode section of the application.  Create a file in the unit tests project folder called TransitModeControllerTest.cs.  After you've completed the code for this file, change the create the TransitModeController.cs file in the Controllers Directory.

using System;
using System.Collections.Generic;
using System.Web;
using System.Web.Mvc;
using MvcApplication.Models;

namespace MvcApplication.Controllers
{
    public class TransitModeController : Controller
    {
        ITransitMode transitModeRepository;

        public TransitModeController(ITransitMode repository)
        {
            this.transitModeRepository = repository;
        }

        [ControllerAction]
        public void TransitModesView()
        {
            // TODO: Add functional code after test is created.
        }
    }
}

Now that you have skeleton of the controller created we have to get a red light test.  Make before going on to the test that the interface is created.  The code for it should look like the listing below.

using System.Collections.Generic;

namespace MvcApplication.Models
{
    public interface ITransitMode
    {
        void Create(TransitMode transitMode);

        IList ListTransitModes();
    }
}

Open that TransitModeControllerTest.cs file back up and add the code listed below.  Once completed run the test, and accordingly it should fail.

using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MvcApplication.Controllers;
using MvcApplication.Models;
using Rhino.Mocks;

namespace MvcApplicationTest.Controllers
{
    [TestClass]
    public class TransitModeControllerTest
    {
        [TestMethod]
        public void TransitModeControllerPassesCorrectViewData()
        {
            MockRepository mocks = new MockRepository();
            ITransitMode transitModeRepository = mocks.DynamicMock();
            SetupResult
              .For(transitModeRepository.ListTransitModes())
              .IgnoreArguments()
              .Return(new List(new TransitMode[] { new TransitMode(), new TransitMode(), new TransitMode() }));
            mocks.ReplayAll();

            TransitModeControllerDouble controller = new TransitModeControllerDouble(transitModeRepository);
            controller.TransitModesView();
            IList transitModes = (IList)controller.RenderedViewData;
            Assert.AreEqual(3, transitModes.Count, "Expected three modes.");
        }

        private class TransitModeControllerDouble : TransitModeController
        {
            public TransitModeControllerDouble(ITransitMode transitMode)
                : base(transitMode)
            { }

            public string SelectedView { get; private set; }
            public object RenderedViewData { get; private set; }

            protected override void RenderView(string viewName
              , string masterName
              , object viewData)
            {
                SelectedView = viewName;
                RenderedViewData = viewData;
            }
        }
    }
}

That gives us our red light test.  Now for the green light.  Add the code in bold below to the TransitModeController.cs file.  Running the test again will give you a green light test.  For now that gives us a good start.

using System.Collections.Generic;
using System.Web.Mvc;
using MvcApplication.Models;

namespace MvcApplication.Controllers
{
    public class TransitModeController : Controller
    {
        ITransitMode transitModeRepository;

        public TransitModeController(ITransitMode repository)
        {
            this.transitModeRepository = repository;
        }

        [ControllerAction]
        public void TransitModesView()
        {
            IList transitModes = transitModeRepository.ListTransitModes();
            RenderView("TransitModes", transitModes);
        }
    }
}

That's it for now.  Merry Christmas, enjoy the code, and I'll take it from this point forward for my next entry!



kick it on DotNetKicks.com
Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

Posted by: Adron
Posted on: 12/25/2007 at 1:11 AM
Categories: How-To, Samples, and Such | Design Patterns
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (1) | Post RSSRSS comment feed