Sunday, May 03, 2009

TypeMock - Are We Missing the Basic Point of Unit Test?

I started to use JMock since five years ago. Later NMock after I started coding in C# and then RhinoMocks. Today I gave TypeMock a look.

Certainly, I love the power that TypeMock brings. I can now Mock static and non-virtual methods that RhinoMocks cannot. This only happens when I'm force to use an invasive, class driven instead of interface driven application framework or 3rd party library that, IMHO, are anti-patterns.

Update (7/1/2009): Given another thought, do I really love the power that can be so easily abused and was massively encourage to do so? I don't think so! Below is a response to a questions about this article on Stack Overflow:

TypeMock website marketing aside, if you are dealing with sealed classes, etc. that you don't control and can't avoid, then it is great to have a tool such as TypeMock. – Troy DeMonbreun

There are many different ways to deal with sealed classes. One way is adapter pattern. Also MVP pattern helps to deal with poorly designed View framework. When my view is passive enough, I can afford not to test it. In almost all the cases when somebody come to me with a need of mocking a sealed class, it was solved by a better design, resulting in cleaner code that is easier to understand, maintain and test. To me, using TypeMock=="Open the door to design flaw" – Kenneth Xu

That is said, there are few important things that I cannot agree with TypeMock's marketing talk. I'm going to write two points today.

Inversion of Control (IoC), also known as Dependency Injection (DI) simplifies software development

I believe to some extend, TypeMock over promoted it's feature and misled developers by giving them false impression to the dependency injection framework. See quoted below.

http://www.typemock.com/Docs/writing_unit_tests_with_isolator.php: special and complex pattern called Inversion of Control ... ... require more development and complicated code that add complexity to development and maintenance

In this software development era, Inversion of Control is as populate, well adopted as Test Driven Design and Development. It is proven to promote better software design, enabling parallel development, simpler code, easier development and maintenance. Today, IoC is no longer special nor complex with the help of established frameworks like Spring.Net Application Framework and Castle Project. Please read on and I'll show you an example.

Unit test with detailed interaction is nothing but fool yourself.

Interaction test isn't necessary bad and sometime it is important. But the database access unit test example on TypeMock completely rewrote the original implementation almost literally one by one to replay the interaction in great detail is a disaster.

Any little change to the implementation can cause the test case to break. So you are force to change the test case for almost anything you did to the implementation code. While this clearly doubles your development work, do you get anything from this?

Did you notice in the video, how many times the presenter had to go back to the original code in order to complete his test case? By taking this approach, will you be able to write the test case first then write your implementation to pass the test case? The answer is simply no. This flat out against Test Driven Design and Development methodology.

Daniel has a very nice post for this. Although I don't necessary agree that all interaction test are bad but I certainly agree that interaction test is widely abused in unit testing. And unfortunately, TypeMock as one of major mock framework, advocating the abuse just to promote their unique feature, is selfish and short sighted.

The same database access code with IoC container

The database access unit test example on TypeMock site contains nothing but anti-patterns

  1. Data access method are static which is extremely hard to be replaced.
  2. I really don't know what to say when a data access method takes a connection string.
  3. reader and command objects are not dispose on exception.
  4. GetUserS actually return one user, always the first user in user table, are you kidding me?

Let's compare it with an implementation user IoC container Spring.Net which provide you with both GetUser and GetUsers. GetUser returns a user object for a given userId and GetUsers returns all users in the database. I believe you can tell which one is more simpler to code and easier to maintain.

    public class AdoDal: IDal
    {
        public virtual IAdoOperations AdoOperations { private get; set; }

        public virtual IList<User> GetUsers()
        {
            return AdoOperations.QueryWithRowMapperDelegate<User>(CommandType.Text,
                "SELECT UserId, UserName FROM dbo.Users", MapUser);
        }

        public virtual User GetUser(string userId)
        {
            return AdoOperations.QueryForObjectDelegate<User>(CommandType.Text,
                "SELECT UserId, UserName FROM dbo.Users WHERE UserId = @userId", MapUser, 
                "userId", DbType.String, 0, userId);
        }

        internal protected virtual User MapUser(IDataReader reader, int rowNum)
        {
            return new User {
                UserId = reader.GetString("UserId"),
                UserName = reader.GetString("UserName")
            };
        }
    }

Same lines of code, but

  1. We implemented two full functional methods instead of just a cripple one.
  2. Spring.Net ensures that reader, command and connection objects are properly disposed and closed.
  3. Works with any database, not just SqlClient.
  4. Each method can be easily tested.
    • Unit test needs no database, any fake, mock or stub would work. Not necessary TypeMock.
    • True integration test is possible and simple with in memory database.

1 comment:

Anonymous said...

Super interesting post. Thanks for sharing

Post a Comment