Использование фабрики для замены экземпляров подделками из Isolation Framework - PullRequest
0 голосов
/ 24 февраля 2010

Месяц назад я закончил читать книгу «Искусство модульного тестирования», и сегодня у меня наконец-то появилось время начать использовать Rhino Mocks с модульным тестированием службы, которая отправляет / получает сообщения на устройства (UDP) и сохраняет / загружает данные из базы данных.
Конечно, я хочу изолировать базу данных и связь UDP.

Например, для доступа к базе данных у нас есть несколько классов со статическими методами, давайте их назовем:

  • AreaADBAccess
  • AreaBDBAccess
  • AreaCDBAccess

Эти классы имели статические методы, которые выполняли доступ к базе данных.
Чтобы иметь возможность заглушить их, я сделал их методы общедоступными виртуальными.

Затем я начал рефакторинг кода, чтобы иметь возможность заменить экземпляр этих классов в коде.
Попробовав разные вещи, я получил фабричный класс, который, кажется, делает все так просто.

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;
    }
}

Тогда по коду я могу использовать

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

А по методу теста я делаю что-то вроде

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

Это первое решение, и я понимаю, что оно может быть сделано лучше.
Можете ли вы прокомментировать это и указать мне лучшие практики?
Можете ли вы также объяснить мне, почему я хотел бы иметь интерфейс вместо определения методов как виртуальных? Кажется бесполезным повторять заголовки метода в 2 местах.
Спасибо!

1 Ответ

4 голосов
/ 24 февраля 2010

Возможно, вы могли бы использовать контейнеры IoC, такие как Windsor, StructureMap или Ninject, которые значительно облегчили бы вашу жизнь, и на самом деле они также работают как «фабрика», которую вы создали вручную, но гораздо более мощны. *

Также есть контейнеры для автозамены, которые будут автоматически создавать фиктивные зависимости для ваших классов, чтобы вы могли сохранить дополнительные строки кода и сделать свой тест менее хрупким.

Что касается вопроса о необходимости интерфейса вместо классов, то в основном это принцип обращения зависимостей, который гласит, что модули (классы) более высокого уровня не должны зависеть от модулей (классов) более низкого уровня, оба они должны зависеть от абстракций. Интерфейсы это те абстракции. Я предлагаю вам взглянуть на принципы S.O.L.I.D, которые сделают вашу жизнь намного проще, по крайней мере, они сделали для меня ..

...