The biggest problem in the way of unit test adoption is dependencies. Initially people are enthusiastic about giving unit testing and TDD a try but fail to apply its principles in real application simply because their code is well, not unit testable. I like Michael Feather's definition of unit test. Unit test is a type of type that

  • Does not talk to database
  • Does not talk to web services
  • Does not touch file system
  • Runs faster than 1/10 second

Ideally, according to the same source and many other similar sources, 60% of testing code should be unit tests, 30% integration and 10% system / user acceptance tests.

We are not there yet. At best what I see people doing is writing a ton of integration tests that create some conditions in the database, execute operation and then read the database state back. The problem with that approach is that integration tests

  • Fragile
  • Require high maintenance (breaks often)
  • Generate many false positives (somebody else changed database)
  • Don't typically have high test coverage
  • Slow

This leaves with three ways forward

Write new code as unit-testable

This may be wishful thinking. You can talk about interfaces and depency injection all day long, but see new code being devloped the old way.

Refactor legacy code It is definitely possible and there are a number of techniques avaialbe. Even though it is a great approach often times there are reasons (mostly excuses) for not doing that.

Use Isolation unit testing frameworks I used a couple of unit testing frameworks that allows to break dependencies without refactoring code, TypeMock and Microsoft Fakes and I was supprised how far TypeMock has gone since I touched it last time.

We have a piece of legacy code with monstorous deep nested tight coupled classes and in a nutshell this is how easy it was to mockup with TypeMock:

    public void AuthenticateAgainstAccount_TypeMock()
            // Arrange
            Isolate.WhenCalled(() => Authentication.Login(userName, password)).WillReturn(loginStoreId);

            Isolate.WhenCalled(() => Account.DataLayer.GetAccount(accountId))
                .DoInstead((ctx) =>
                   return GetFakeAccount(accountId);

            // Act
            int response = Authentication.AuthenticateAgainstAccount(appId, auth);

            // Assert
            Assert.AreEqual(response, returnAccountId);

        private Account GetFakeAccount (int accountId)
            var account = Isolate.Fake.Instance<Account>();
            Isolate.WhenCalled(() => account.AccountId).WillReturn(accountId);
            Isolate.WhenCalled(() => account.Store.StoreId).WillReturn(123);
            return account;

This bypases multiple nested classes, database connections, configuration, web services. Everything that method under test depends on.

TypeMock isn't a cheap tool ($999 for build server license and $399 for developer license), but the alternative would be to upgrade to higher SKU of Visual Studio which would cost even more. I will begin wearing their T-Shirt which says "Legalize unit testing" on the back.