var blog = new ThoughtStream(me); RSS 2.0
 Monday, March 31, 2008

A while back, I posted a stream of thoughts concerning programming in triples. The basic idea is that we have 2 developers doing pair programming and a third developer doing automated acceptance test development, on the same story at the same time.

Triples Programming Terms

I've been discussing this idea with various people since then, and I'm finding myself referring to this team organization with a specific term, now:

Pair+1 Programming

I don't know if this term has been used previously, or if there is already a term for this. However, I find this to be a good name for the organization that I am talking about.  It really is pair programming, as defined by many common sources, plus one acceptance test developer. The really fun part is that all three of the developers in this unit will switch roles throughout the day. The end result is that we have a very well cross-trained team - each member of each unit will be responsible for writing unit tests, writing production code, writing acceptance tests, and thinking ahead of the current code, throughout any given day.

An Agile Team includes more than just the developers - the customers, the test lead, the technical writers, the project manager, etc., are all part of the team. Pair+1 Programming is not into teams. A "team" implies that they the persons involved are fairly static - they don't move between teams, etc. In order to distinguish the Pair+1 organization from the Team as a whole, I am also going to refer to this group with another name:

Development Unit (Update: based on further discussion, renaming this)

WorkCell

Just as a developer will rotate rolls within the Unit, I believe Pair+1 programmers should move between WorkCells on a regular basis. This will further the cross-training that occurs and help to pollinate the knowledge of one WorkCell into the thoughts and goals of other WorkCells. The migration is likely to be less volatile than the role switching that happens within a WorkCell. For example, developers rotate positions every 2 or 3 hours, within the WorkCell, whereas a developer may only rotate between WorkCells once a day or once a week.

Development Systems

Within each Development Unit, there will be two development systems.

  1. The primary system of that Unit will be the pair programming system. This system should have a very large monitor - 30" is preferable - to allow the Pair to easy see everything on the screen.
  2. The secondary system will be used for Acceptance Tests and is likely to only need a 22" or 24" monitor, as it will be primarily used by a single developer.

Each of these systems, aside from monitor size, should be a mirror of each other - the same system specs with the same software development packages installed. Every machine used for development should be able to execute any of the tests - unit tests or acceptance tests - no matter the primary use of that development machine. This is required so that the Pair working on the Development machine can execute the Acceptance Tests being created on the Acceptance Test machine; thus ensuring that they are coding appropriately, and passing the expected tests. The Acceptance Test developer should also be able to execute the code being written by the Pair, to ensure that the Acceptance Tests are being written against the API correctly.

Following standard Agile practices, these machines should not be concerned with Email, IM, or other non-development tasks. They should be dedicated entirely to development work. If a person needs Email, IM, etc. then the office space should provide access via shared systems and/or a developer should have their own system (likely a laptop) that they can take with them wherever they go. The point is, though, to remove distraction from the Development Unit. Keep the non-development software off the Unit's systems, as much as possible, to ensure that the Unit can focus on development and not be distracted by 50 different flashing icons and windows on the machine.

Disclaimer

A lot of my thoughts are purely academic, at this point. I am hopefully going to have some actual experience with this environment, soon. As such, I am trying to organize my thoughts so that I can have some direction to start with. I reserve the right to be wrong and change my mind whenever I want, likely re-defining the terms and processes involved in Pair+1 Programming.

Monday, March 31, 2008 2:26:19 PM (Central Standard Time, UTC-06:00)  #    Comments [0]. Trackback 
Tags: Acceptance Testing | Agile | Behavior Driven Development | Management | Pair+1 Programming | Unit Testing

In my previous post, I talked about my base repository class that I use, to abstract the NHibernate details away from the actual repository. Now that I have the Do and DoTransaction methods to further the abstraction, I thought it would be good to share my whole abstraction.

using System;
using NHibernate;
using NHibernate.Cfg;
 
namespace RepositoryBase
{
 
    public abstract class Repository : IDisposable
    {
 
        #region Vars
 
        private static readonly Configuration _config;
 
        private static readonly ISessionFactory _factory;
 
        #endregion
 
        #region Constructor / Destructor
 
        static BaseDAL()
        {
            _config = new Configuration();
            _config.Configure(typeof(BaseDAL).Assembly, "RepositoryBase.hibernate.cfg.xml");
            _factory = _config.BuildSessionFactory();
        }
 
        ~BaseDAL()
        {
            Dispose();
        }
 
        #endregion
 
        #region Properties
 
        public ISession Session { get; private set; }
 
        public ITransaction Transaction { get; private set; }
 
        #endregion
 
        #region Methods
 
        protected void Do(Action unitOfWork)
        {
            try
            {
                OpenSession();
                unitOfWork();
            }
            finally
            {
                CloseSession();
            }
        }
 
        protected void DoTransaction(Action unitOfWork)
        {
            try
            {
                OpenSession();
                BeginTransaction();
                
                if (unitOfWork != null)
                    unitOfWork();
                
                CommitTransaction();
            }
            catch
            {
                RollbackTransaction();
                throw;
            }
            finally
            {
                CloseSession();
            }
        }
 
        #endregion
 
        #region Helper Methods
 
        private void OpenSession()
        {
            if (Session != null) return;
 
            Session = _factory.OpenSession();
            Session.FlushMode = FlushMode.Auto;
        }
 
        private void CloseSession()
        {
            if (Session == null) return;
 
            if (Session.IsOpen)
            {
                Session.Close();
            }
            Session.Dispose();
            Session = null;
        }
 
        private void BeginTransaction()
        {
            ValidateSession();
 
            Transaction = Session.BeginTransaction();
        }
 
        private void CommitTransaction()
        {
            ValidateSession();
 
            if (Transaction != null)
                Transaction.Commit();
 
            CloseTransaction();
        }
 
        private void RollbackTransaction()
        {
            if (Transaction != null)
                Transaction.Rollback();
 
            CloseTransaction();
        }
 
        private void ValidateSession()
        {
            if (Session == null)
                throw new ApplicationException("NHibernate Session Not Open.");
        }
 
        private void CloseTransaction()
        {
            if (Transaction == null) return;
 
            Transaction.Dispose();
            Transaction = null;
        }
 
        #endregion
 
        #region IDisposable Members
 
        public void Dispose()
        {
            CloseTransaction();
            CloseSession();
        }
 
        #endregion
 
    }
}

Note that I am using an embedded resource as my "hibernate.cfg.xml" location. Just change this line to use a file system resource, or whatever you want for your hibernate configuration.

I'm still wanting to re-abstract this into a set of objects that let's me be concerned with transactions and queries at a business level. One step at a time, though. The syntax that I would like to see, at the moment, would be something like this:

public void SomeBusinessValue()
{
    Something something = DoSomething.BusinessRelated();
    SomethingElse somethingElse = SomethingElse();
    Repository.DoTransaction(() =>
    {
        SomeRepository.Save(something);
        SomeOtherRepository.Delete(somethingElse);
    });
}

And if I get really ambitious, I may try to incorporate Castle.Windsor's Automatic Transaction Facility, so that I can have syntax like this:

[Transactional(Transaction.Requires)]
public void SomeBusinessValue()
{
    Something something = DoSomething.BusinessRelated();
    SomethingElse somethingElse = SomethingElse();
 
    SomeRepository.Save(something);
    SomeOtherRepository.Delete(somethingElse);
}

Of course, the more I travel down this path, the more obvious it is how Rhino.Commons' Repository came along. Heh - I'm only about 2 steps away from completely re-doing Ayende's UnitOfWork.

In fact, I may try to re-use NHibernateQueryGenerator and go for this syntax:

[Transactional(Transaction.Requires)]
public void SomeBusinessValue()
{
    Something something = Repository<Something>.Find(Where.SomeProperty = someValue);
 
    Something something = DoSomething.BusinessRelated();
    SomethingElse somethingElse = SomethingElse();
 
    Repository<Something>.Save(something);
    Repository<SomethingElse>.Delete(somethingElse);
}

Wouldn't that be fun... I really do like this syntax. In fact, I spent 9+ months working with it on a project because I love the simplicity of the syntax. In the end, though, I did not understand all of the underlying architecture and abstraction and Ayende set up (he includes the ability to plug in any DAL, including Castle.ActiveRecord, NHibernate, etc) and it caused headaches for me.

...

The major problem I have with this syntax, at the moment, is unit testing the Repository object. I either need to hide these details behind an IWhateverRepository interface (complete with Save, Delete and GetByWhatever methods) like I have been doing, or I I'll need to learn how to unit test NHibernate with in-memory database or something... We'll see where this leads.

(side note: how's that for a "ThoughtStream". )

Monday, March 31, 2008 1:22:29 PM (Central Standard Time, UTC-06:00)  #    Comments [0]. Trackback 
Tags: .NET | Agile | Data Access | NHibernate | Unit Testing | UnitOfWork

For the last few months, I've had a very small base class that abstracts out the NHibernate configuration, session creation, etc. It works very well, but is very limited in what it can do. basically, every method in my actual repository implementation would have to open a new session, execute a criteria and close the session. A typical implementation would look like this:

public ICollection<Invoice> GetAll()
{
    ICollection<Invoice> invoices = null;
    try
    {
        OpenSession();
 
        invoices = Session.CreateCriteria(typeof(Invoice)).List<Invoice>(); 
    }
    finally
    {
        CloseSession();
    }
 
    return invoices;
}

That certainly is easy and keeps the code fairly clean, removes a lot of duplication, etc.

However, there is a significant limitation - I can't have any code re-use for multi-query scenarios, without duplicating code. In other words, if I want to load that list of invoices and then load some other collection from another repository, I have to use two different Sessions across two different repository implementations. This really becomes an issue when dealing with transactions - I want my entire change set to pass or fail in a single transaction. In my current abstraction, this can't be done.

Fortunately, NHibernate has the solution to my dilemma built right in - all I need to do is create my criteria objects without a session, and then I can execute any / all of them from any session that I want.

public ICollection<Invoice> GetAll()
{
    ICollection<Invoice> invoices = null;
    try
    {
        DetachedCriteria criteria = DetachedCriteria.For<Invoice>();
        
        OpenSession();
        invoices = criteria.GetExecutableCriteria(Session).List<Invoice>(); 
    }
    finally
    {
        CloseSession();
    }
 
    return invoices;
}
 

I don't have a complete abstraction of the separate execution, yet. However, a very basic implementation could look like this (idea stolen from Ray Houston):

protected void Do(Action unitOfWork)
{
    try
    {
        OpenSession();
        unitOfWork();
    }
    finally
    {
        CloseSession();
    }
}
 
public ICollection<Invoice> GetAll()
{
    ICollection<Invoice> invoices = null;
 
    DetachedCriteria criteria = DetachedCriteria.For<Invoice>();
 
    Do(() =>{
        invoices = criteria.GetExecutableCriteria(Session).List<Invoice>();
    });
    
    return invoices;
}

This simple abstraction provides a lot of benefit for us.

  • Eliminates duplicate code (not calling OpenSession / Close Session from all repository methods)
  • Removes ugly Try / Catch blocks from Repository methods
  • Allows multiple Criteria to be executed from a single Session / Transaction

And most importantly - this gives us the ability to create a better abstraction of NHibernate, to support business level transactions, not just repository level transactions. Obviously, this simple example is not going to provide business level transactions. It does get us down the path, though.

Monday, March 31, 2008 10:29:10 AM (Central Standard Time, UTC-06:00)  #    Comments [1]. Trackback 
Tags: .NET | Agile | Data Access | NHibernate | Refactoring | UnitOfWork

 Wednesday, March 26, 2008

In the last few months, I've heard a person say this multiple times

"business runs on de-normalized data"

The context of this statement has always been the discussion of database design and implementation... and I continue to wonder where this person's experience is coming from, to make such a statement. In my world - the business of developing software to run a business or automate a portion of a business - this is very far from the truth. My response to this statement is (and yes, you can quote me on this - please do)

Management reports on de-normalized data, but operations runs entirely on well-normalized data

I'm not going to make any ignorant or naive claims about de-normalized data having no place in business. There certainly is a lot of business value in de-normalized data. That's why we have data warehousing, OLAP cubes, and other reporting database structures (including Views, Stored Procedures, etc. that will de-normalize data for live reporting). When it comes to the day to day business, though - the people on the floor doing the low level business work - well normalized data is an absolute must.

From WikiPedia's entry on Database Normalization:

Database normalization, sometimes referred to as canonical synthesis, is a technique for designing relational database tables to minimize duplication of information and, in so doing, to safeguard the database against certain types of logical or structural problems, namely data anomalies. For example, when multiple instances of a given piece of information occur in a table, the possibility exists that these instances will not be kept consistent when the data within the table is updated, leading to a loss of data integrity. A table that is sufficiently normalized is less vulnerable to problems of this kind, because its structure reflects the basic assumptions for when multiple instances of the same information should be represented by a single instance only.

I'm certainly not a DBA and I'm not a real database guru. I do have more than 10 years experience working with various database systems (SQL Server, Access, Oracle, SQLite, MySQL, DB2/DB400, and XML & flat files , etc.) in various business scenarios (manufacturing, engineering, business process automation, e-commerce/e-business, enterprise integration, etc. etc.) and I have a pretty high opinion of my relational modeling capabilities ... and It evokes a sense of disbelief and shock when I see poorly normalized schema's or hear statements like this being made.

Please, please, PLEASE take the time to understand what well normalized database design is and why it's necessary for flexible, maintainable software.

Wednesday, March 26, 2008 12:38:04 PM (Central Standard Time, UTC-06:00)  #    Comments [0]. Trackback 
Tags: Data Access | Database Design | Management

 Monday, March 24, 2008

This might be common knowledge for those that use NHibernate - but it's new to me, since I am not an NHibernate power-user, yet.

Given this object model:

public class Invoice
{
    private int _id = 0;
    private string _invoiceNumber = string.Empty;
    private DateTime _invoiceDueDate = DateTime.MinValue;
 
    private ICollection<InvoiceDetail> _invoiceDetails = new HashedSet<InvoiceDetail>();
 
    public int Id
    {
        get { return _id; }
    }
 
    public string InvoiceNumber
    {
        get { return _invoiceNumber; }
        set { _invoiceNumber = value; }
    }
 
    public DateTime InvoiceDueDate
    {
        get { return _invoiceDueDate; }
        set { _invoiceDueDate = value; }
    }
 
    public ICollection<InvoiceDetail> InvoiceDetails
    {
        get { return _invoiceDetails; }
    }
 
    public InvoiceDetail CreateDetail()
    {
        InvoiceDetail detail = new InvoiceDetail();
        InvoiceDetails.Add(detail);
        return detail;
    }
 
}
 
 
public class InvoiceDetail
{
 
    internal InvoiceDetail() { }
 
    private int _id = 0;
    private string _productName = string.Empty;
    private decimal _productCost = 0;
    private int _productQuantity = 0;
 
    public int Id
    {
        get { return _id; }
    }
 
    public string ProductName
    {
        get { return _productName; }
        set { _productName = value; }
    }
 
    public decimal ProductCost
    {
        get { return _productCost; }
        set { _productCost = value; }
    }
 
    public int ProductQuantity
    {
        get { return _productQuantity; }
        set { _productQuantity = value; }
    }
 
}

We can load any invoice that has an Invoice Detail with a specific Product Name, using this NHibernate code:

invoices = Session.CreateCriteria(typeof(Invoice))
    .CreateCriteria("InvoiceDetails")
    .Add(Expression.Eq("ProductName", productName))
    .List<Invoice>();

Additionally, we can add paging to the result set, by including the SetFirstResult and SetMaxResult calls in the Criteria.

invoices = Session.CreateCriteria(typeof(Invoice))
    .CreateCriteria("InvoiceDetails")
    .Add(Expression.Eq("ProductName", productName))
 
    .SetFirstResult(pageSize * pageNumber)
    .SetMaxResults(pageSize - 1)
 
    .List<Invoice>();

Notice the use of pageSize and PageNumber to set the First Result value - this creates an index-by-zero page number that we want to view. For example, if our page size is 10 and we are on page zero, then the FirstResult is going to be 0 and the last result will be 9. 0 through 9 = 10 results. If we are on page 3, then the FirstResult is 30 and the MaxResult is 39...

Works pretty well... now, let's just hope that the actual underlying database implementation of this can take advantage of each DBMS' optimizations for doing this.

Monday, March 24, 2008 4:39:52 PM (Central Standard Time, UTC-06:00)  #    Comments [0]. Trackback 
Tags: .NET | Data Access | NHibernate

If you are considering the use of NHibernate, or are already using NHibernate, be sure that you always override the .Equals and .GetHashCode methods of your entities. NHibernate makes use of these methods extensively and you are likely to have strange issues if you don't override both of these correctly. Unfortunately, GetHashCode is one of those areas that is difficult to get right; but it needs to be done anyways.

For more information, see:

And one specific note on .Equals: be sure to check ReferencEquals as the last resort, if no other comparison is possible.

I've learned these lessons the hard way; hopefully you won't have to.

Monday, March 24, 2008 2:29:02 PM (Central Standard Time, UTC-06:00)  #    Comments [2]. Trackback 
Tags: .NET | NHibernate

In WCF, when using NetDataContractSerializer to enable .NET Remoting, DataContract objects are still serialized. Only ServiceContract objects are marshaled ByRef. The same setup is possible in native .NET Remoting, as well. It seems more likely to happen in WCF, though. Based on my experience, Remoting is usually all MarshalByRef or all Serialized - but that's just my experience. Either way, if you are serializing your object model, you need to be careful.

Parent <-> Child Bidirection Relationships

If you have a Parent<->Child bidirectional relationship in your DataContract objects, when you send the DataContract objects across WCF, they will be serialized/deserialized and the resulting hierarchy of objects will be: Parent->Child->CopyOfParent. This causes problems when using NHibernate to auto save/load your object tree.

For example, this object hierarchy:

Parent
  |--Child
  |     |--Parent (reference to actual Parent)

Will end up looking like this, after being serialized / deserialized:

Parent
  |--Child
  |     |--CopyOfParent (new object, independent of actual Parent)

To fix this, you'll have to manually re-build your references, after the objects are deserialized:

foreach(Child child in parent.Children)
{
    child.Parent = parent;
}

If you don't rebuild the hierarchy references like this, NHibernate will not save or update your child objects correctly. You will either get "Transient Instance" exceptions or you will end up with orphaned children records because they will not have their parent id set correctly.

Here's an example of what one of my data access methods looks like, in an app that remotes the data access layers via WCF (and hides most of the NHibernate code in a base class):

public void Save(FooBar fooBar)
{
    try
    {
 
        foreach(Widget widget in fooBar.Widgets)
        {
            widget.FooBar = fooBar;
        }
 
        OpenSession();
        BeginTransaction();
        Session.SaveOrUpdate(fooBar);
        CommitTransaction();
    }
    catch
    {
        RollbackTransaction();
        throw;
    }
    finally
    {
        CloseSession();
    }
}

Extended Problem: Multi-Parented Children

Although I haven't run into this situation yet, I am assuming that the same problem will occur if you have multiple parents pointing to the same child. For example, if you have:

Parent
  |--Child1
  |     |--GrandChild1 (same reference as Child2's GrandChild)
  |--Child2
  |     |--GrandChild1 (same reference as Child1's GrandChild)

When you send this structure across WCF as a set of DataContract objects, I imagine that you will end up with this:

Parent
  |--Child1
  |     |--GrandChild1 (duplicate of Child2's GrandChild)
  |--Child2
  |     |--CopyOfGrandChild1 (duplicate of Child1's GrandChild)

If your intention is to have Child1 and Child2 reference the same record in the database, you will need to reconstruct the Child1 and Child2 references to GrandChild1, ensuring that both point to the same object. I can see the basic code as traversing the children and comparing each of the gradchildren's values, then picking one winner between the same values and resetting the references on the rest of them. Unfortunately, I think the solution for this scenario would likely be unique to each situation, due to the complexity of picking the correct reference.

Has anyone run into this situation? If so, how did you solve it?

Monday, March 24, 2008 2:01:16 PM (Central Standard Time, UTC-06:00)  #    Comments [2]. Trackback 
Tags: .NET | NHibernate | WCF

 Monday, March 17, 2008

A few people have asked what my blog setup is, in respect to screen shots and code samples, so here's the answer for all to see.

Blog system: dasBlog

I love the simplicity of dasBlog. No extra fluff, no massive database or configuration system. It's just a blog with the features that I want, and it stores all of it's settings and content in XML files.

Be sure to change the web config so that the trust level is "full" and not "medium". Otherwise the blog posting API won't allow you to upload images and other attachments.

Web Host: WebHost4Life

A good .NET hosting company. I have the $10/month account type. There are plenty of other (and probably better) .NET hosting companies out there.

Screen Shots: a custom app I wrote 4 years ago, called ScRap.NET

It's easy to use. just hit the "PrtScn" (print screen) button on your keyboard, then click-n-drag with your mouse. The gray box that draws on your screen is where the screen shot will come from.  There are professional options for this, though - such as SnagIt. The reality of it is that it doesn't matter what you use to create the screen shots. The reason mine look like they do is because of my post authoring tool.

Post Authoring Tool: Windows Live Writer

An off-line blog editing tool (which I am using to write this post). It supports most of the blogs out there - the major ones anyways; and has lots of nice little features to make post writing simple. It has it's limitations (not all HTML is supported or easy to do) but if you can live with the formatting limitations, it produces some high quality work in an offline, "draft"-able manner. It supports screen shots, file attachments and some other cool stuff.

Check out the plugins for it, to add a lot more fun stuff.

Code Format Tool: Code Snippet Plugin

All the beauty of my code examples come from this plugin. Just paste some text into the plugin and set your formatting options.

And that's it... a pretty simple setup that does exactly what I want in my blog.

Monday, March 17, 2008 3:33:44 PM (Central Standard Time, UTC-06:00)  #    Comments [0]. Trackback 
Tags: General

 Sunday, March 16, 2008

A coworker and I often have conversations about Unit Testing vs. Test Driven Development. Generally speaking, we agree - there are some semantic or mechanical differences in what we're saying, but nothing major and we usually work that out through the conversations, defining what we are saying. Recently he asked if I ever allow myself to write any code without unit tests, or write code before unit testing it. My initial answer was no, not surprisingly. However, after discussing the question and it's implications further, he brought up a good point and a scenario where I highly encourage writing code without tests:

Prototyping (or Spiking, in Agile terms).

I've posted in the past about how I believe that Prototyping A Process is important in software development, so I won't completely re-hash that. Although, the language that I use to describe prototyping may be evolving, the core concepts and process are still in place (the spiking concept is the same as what I called Prototyping).

Here's what ExtremeProgramming.com has to say about Spiking:

"Create spike solutions to figure out answers to tough technical or design problems. A spike solution is a very simple program to explore potential solutions. Build a system which only addresses the problem under examination and ignore all other concerns. Most spikes are not good enough to keep, so expect to throw it away. The goal is reducing the risk of a technical problem or increase the reliability of a user story's estimate.

 

When a technical difficulty threatens to hold up the system's development put a pair of developers on the problem for a week or two and reduce the potential risk. "

This may seem counter to the creed of writing unit tests first and even counter to the creed of not coding for the future. There is a key element in this description, which I believe is not emphasized nearly enough. The code in your spike IS throw-away code. DO NOT copy and paste even one line of code from the spike into the production code.

"Copy and paste is a design error." - David Parnas

When you understand the process, technology or whatever it is that you are learning, well enough, you must step back from that solution and back into your actual project. Then, you continue the test-first process of Test Driven Development - you write your tests for the area that you are covering and then you write the implementation code using the spike as a read-only reference.

So, yes - there is a time and place for writing code without any unit tests; production code is never that place, though.

Sunday, March 16, 2008 11:17:45 AM (Central Standard Time, UTC-06:00)  #    Comments [0]. Trackback 
Tags: Agile | Test Driven Development | Unit Testing

 Thursday, March 13, 2008

A lot of people ask these questions when they first start unit testing

  • How many unit tests is too many?
  • Do I need to cover every property, every individual method, ever object, every ???

The goal of unit testing is to provide 100% test coverage. The reality of unit testing is that you want 95% or more, test coverage. There are occasions when unit testing that one last line of code is horrendously repetitious or you miss something or accidentally couple something too tightly. But wait... there's more... and those seem like lousy excuses that lead to allowing bad design in your code.

Ultra-Fine Granularity is Horrible

If you are writing your unit tests after you write your production code, or if you are writing your unit tests first but are simply going through the mechanical process switch and it doesn't really matter if you write your tests first or not, then the answer is horrible. You'll end up unit testing way more than you need to. For example, I wrote a login screen last year. This login screen has three fields and two buttons on it: Username, Password, a drop list of locations assigned to the username, a Login button and a Cancel button. How many unit tests do you think should be written for this? ... I wrote 27 unit tests to cover every possible edge case in the presenter that controlled this view. What a giant horrible mess - changing anything in that login screen was almost as bad as not having it unit tested at all (well ok... nothing is that bad)

I ended up unit testing setting an individual property, and then checking to make sure that property was stored correctly. I unit tested individual method calls with only the username set, or only the password set, or only the location set, or only whatever combination of those set. I unit tested loading the list of locations for the username, and ensuring that the location selected is valid for the user. I unit tested what would happen is an invalid location was selected or a null location was selected... every possible edge case was unit tested and it drove bad design into the application because no one wanted to go through the pain of having to change all of those unit tests at that level of granularity.

Step Up To The API

Just unit testing your code is a great way to ensure that you are writing way more unit tests than you need. Chances are, the code you are writing is not very cohesive and you will end up unit testing the read and write of individual properties rather than just unit testing the business value (process) that actually reads / writes the individual properties. That is to say, your unit tests should be written at one or two steps above ultra-fine granularity. Don't test the individual properties, test that API that you want to call, that has business value.

So, how do you account for 100% code coverage if you are not unit testing the properties and all of the edge cases?

Never write code that you don't need, right now. If you are writing a unit test and the test or the implementation needs a property, then you create that property for that unit test at that time. This does not mean that you write a bunch of get / set property unit tests, just so you can unit test the properties. This means that you specify the business value API in your unit test, and by virtue of having business value, you will likely have various properties associated with the classes in that API. The same is true for edge cases - if the business value of the unit test does not handle the edge cases, then there are no edge cases. Only when you have business value specifying an edge case, do you need to write a unit test for the edge case and possibly modify code to handle the edge case.

Ok, then what happens if your code changes and you don't call that property in the original unit test, anymore?

Never leave dead code in your system. Ever. Period. End of discussion. If you change your unit tests because the design of the object(s) change, and you are no longer using a property - delete the property! If you delete it and you find that you can't compile the code any longer because other parts of the system need that property, then you need to evaluate whether or not that property is really providing value to those other places vs. changing those other places to match the new design.

Test First vs. Test After

A big part of figuring out how many unit tests you need is understanding the functionality of the system. You should be writing a unit test for every functional point of the code, achieving 100% code coverage. The problem with the original question of how many unit tests to write, though, is that there is a hidden assumption in that question:

"I wrote my code, now how many tests do I need, to cover it correctly?"

This question is an underlying problem in Unit Testing and simple Test First development. If you are just unit testing your existing code or only going through the mechanical process switch of writing a unit test first, but not really using the test to drive your design, then you are likely not going to see some of the major benefits of Test Driven DESIGN / Development: not writing code you don't need, and creating the API that you want to call instead of the API coming together haphazardly as a bi-product of writing code first.

When you take the step up to unit testing the API, it becomes more apparent that you really want to specify the API before you write it. If you specify the API before you write it, then you are one step closer to true Test Driven Development. Don't expect the test to design your code for you. Use the test to flesh out your design before you write any code.

Test Driven DESIGN / Development

Would you rather:

Write 50+ lines of code into your model, then write a unit test that shows an ugly API causing you to go back to the code and re-write it in the hopes that it will produce a better API, most likely repeating this process once or twice until you get frustrated with changing your code because it takes so long

or

Write 5 lines of unit test code, specifying the API that you want, realizing that it's not going to work and changing 2 or lines of that test, going through this cycle 5 or 6 times until you have the API that you really do want to call; then implementing the API in the 50+ lines of code and being done with it

I'll take #2. I don't like rewriting large chunks of code. Rewriting 2 or 3 lines of code is easy - I'll do that any minute of any day. Chances are, if you are willing to write the correct number of unit tests by specifying the higher level API in your unit tests, you will gravitate toward designing your API in your unit tests.

TDD Misconception:

TDD is NOT a design tool. It is not "the answer". Is will not design your application for you. It will not solve your problems for you. If you don't know how to design software, then you need to get some training on design patterns, loose coupling through single responsibility and separation of concerns, and various other core foundations of good Object Oriented Development.

In reality, Test Driven Development is just an easier way of saying this:

"Design your API in the context of a unit test, so that you have your API implementation covered by unit tests before you even write the implementation."

Conclusions:

In the end, we can answer the original questions from this post by re-stating Test Driven Development as a software development guideline:

"Design via code, unit testing 100% as you go."

Thursday, March 13, 2008 1:45:25 PM (Central Standard Time, UTC-06:00)  #    Comments [0]. Trackback 
Tags: .NET | Agile | Model-View-Presenter | Test Driven Development | Unit Testing

Navigation
About Me
View Derick Bailey's profile on LinkedIn

Send mail to the author(s) Contact Me
Archive
<March 2008>
SunMonTueWedThuFriSat
2425262728291
2345678
9101112131415
16171819202122
23242526272829
303112345
About the author/Disclaimer

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

© Copyright 2010
Derick Bailey
Sign In
Statistics
Total Posts: 115
This Year: 0
This Month: 0
This Week: 0
Comments: 44
Themes
Pick a theme:
All Content © 2010, Derick Bailey
DasBlog theme 'Business' created by Christoph De Baene (delarou)