views:

31

answers:

1

A month ago I finished reading the book "Art of Unit Testing" and today I finally had time to start using Rhino Mocks with unit testing a service that sends/receives messages to devices (UDP) and saves/loads data from the database.
Off course I want to isolate the database and the UDP communication.

For example for database access, we have some classes with static methods, let's call them:

  • AreaADBAccess
  • AreaBDBAccess
  • AreaCDBAccess

These classes had static methods that executed the database access.
To be able to stub them I made their methods public virtual.

Then I started refactoring the code to be able to replace the instance of these classes on the code.
After trying different things, I ended up with a factory class that seems to make things so easy.

public class ObjectFactory
{
    private static Dictionary<Type, object> Instances = new Dictionary<Type, object>();

    public static T GetInstance<T>() where T : new()
    {
        if(Instances.ContainsKey(typeof(T)))
            return (T)Instances[typeof(T)];

        return new T();
    }

    public static void SetInstance<T>(object obj)
    {
        Instances[typeof(T)] = obj;
    }
}

Then on the code, I can use

private AreaADBAccess DBAccess = ObjectFactory.GetInstance<AreaADBAccess>();

And on the test method I do something like

AreaADBAccess dbAccess = mocks.Stub<AreaADBAccess>();
using (mocks.Record())
{
...
}
ObjectFactory.SetInstance<AreaADBAccess>(dbAccess);
//Invoke the test method
...

This is a first solution and I realize that it can be done in a better way.
Can you now comment it and point me to the best practices?
Can you also explain to me why I would want to have an interface instead of defining the methods as virtual? It seems useless to repeat the method headers in 2 places.
Thanks!

+4  A: 

Probably you could make use of IoC containers like, Windsor, StructureMap or Ninject, which would make your life much easier, and in fact they also work as "factory" you have created manually, but are much more powerful.

Also there are automocking containers which will automatically create mock dependencies for your classes so you can save additional lines of code, and make your test less fragile.

As for question regarding the need for interface instead of classes, its basically the Dependency Inversion Principle, which states that higher level modules (classes) should not depend on lower level modules (classes), both of them should depend on abstractions. Interfaces are those abstractions. I suggest you would take a look at S.O.L.I.D principles which will make your life much more easier, at least they did to me..

miensol
+1. Yes, it seems @pauloya implemented his own Service Locator. See http://www.hanselman.com/blog/ListOfNETDependencyInjectionContainersIOC.aspx for a list of IoC Containers.
TrueWill
If you keep an interface you need to maintain yet another file with the methods of the classes.. But I guess you can use refactoring tools to help you with the maintenance?
Paulo Manuel Santos
I guess having one more file for interface is a zero cost. Especially if it makes your design more flexible and extensible, which lies in the roots of Dependency Inversion principle...Tools like Resharper, CodeRush or event Vistual Studio built in refactoring tool make easier to manage those files...
miensol
@miensol: +1 for mentioning SOLIDs
Igor Brejc
@paulouya, what do you prefer: a single class 10.000 lines of mangled and intertwined code or 50 files of small and easily understandable interfaces and classes with clear responsibilities? You should stop worrying about creating new source files - small classes are your friends. And you're right: refactoring tools like R# make managing these files much easier.
Igor Brejc
@Igor, I don't see why the option has to be a huge single class, the way I've done it I have several small database access classes that contain public virtual methods. I also understand that if you want to have the option of pluging different classes into your system, IoC and interfaces make sense. But my only objective, for now, is to be able to isolate my code from these database access classes with Rhino Mocks. But I did open this question to learn from you guys, so I thank you for your input and I promise to go learn more about IoC and SOLID.
Paulo Manuel Santos