Код модульного тестирования, который вызывает статические методы - PullRequest
7 голосов
/ 08 ноября 2011

Я прочитал большинство вопросов, связанных с SO ( здесь , здесь и там ).Последний вопрос предлагает четыре альтернативы для создания кода, который вызывает статические методы для модульного тестирования.Я хочу спросить о моем конкретном случае: у нас есть проект «уровень бизнес-логики» или «правила», который содержит 45 статических классов (без состояния, только статические методы).Более того, они сами по себе не легко тестируются: большинство из них имеют доступ к базе данных и файловой системе.В любом случае, это не так уж и плохо: для доступа к базе данных они используют уникальный экземпляр некоторого класса Mapper (все Mappers являются синглетонами).Всякий раз, когда я пытаюсь что-то тестировать, я сталкиваюсь с этой стеной.Самая большая проблема в том, что это очень, очень важный код, и изменения в нем должны планироваться очень тщательно.Мой вопрос: как мне сделать этот прибор более тестируемым?Должен ли я написать 45 интерфейсов и использовать внедрение зависимостей?И все же, как мне заглушить / смоделировать Mappers?

PS: Я читал «Работа с устаревшим кодом» Майкла Фезерса, поэтому прямые ссылки приветствуются (и другие книги тоже)* Редактировать : Поскольку некоторые люди говорили, что решения могут зависеть от платформы, я работаю над .NET (C # и некоторыми VB.NET)

Ответы [ 5 ]

10 голосов
/ 08 ноября 2011

Текущая ситуация, вероятно, заключается в том, что никто не осмеливается что-либо менять в коде, потому что это может привести к непредсказуемым последствиям.Убедитесь, что все понимают, что вы улучшаете ситуацию: ваши изменения могут нарушить код, но в отличие от ранее , эти поломки будут найдены и один раз они были найдены, они будут исправлены навсегда .

Тем не менее, следующий шаг зависит от вашего опыта и вашей репутации в команде.Если вы хотите быть осторожнее, используйте такой код (синтаксис Java):

Mapper {
    public static Mapper INSTANCE = new Mapper(); // NEW code

    protected void doImpl() { // NEW CODE
        ... code copied from impl()... // OLD code, NEW PLACE
    }

    public static void impl() { // OLD code
        INSTANCE.doImpl(); // NEW code
    }

    // OLD code ...
}

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

Таким образом, вы можете заменять один метод за раз.Вы можете в любой момент прекратить этот путь - каждое изменение занимает всего пару минут, и это рефакторинг: код делает именно то, что делал раньше.Поскольку каждое изменение настолько мало и ничего не может сломать, вы можете заменить один метод, написать все модульные тесты, которые вы не могли написать раньше, промыть, повторить.Наконец, если вы не хотите / не нужно переделывать все статические методы, этот подход предоставляет вам все возможности, о которых вы могли бы просить.

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

Если вы начали с DI, вам пришлось бы изменить много кода во всехвиды мест - без надлежащих юнит-тестов, которые могли бы вас защитить.

3 голосов
/ 08 ноября 2011

Создайте интерфейс класса Mapper и замените функциональность mapper новой реализацией moc. Я думаю, что вам нужно будет реализовать абстрактную фабрику, которая будет генерировать экземпляры Mappers. Поэтому создайте IMapperFactory, и две реализации этого DBMapperFactory и MocMapperFactory передают экземпляр этого объекта вашему коду, где вы получаете доступ к Mappers и генерируете их с использованием этого экземпляра.

Gl.

2 голосов
/ 08 ноября 2011

Ваш вопрос действительно связан с платформой, в мире Microsoft вы можете использовать Microsoft Moles для тестирования статических методов. И издеваться над статическими методами.

В мире Java, вероятно, есть другие инструменты, или вам следует избегать использования статики.

На других платформах есть другие инструменты и т. Д.

В общем, любой статический метод сделает ваш код менее тестируемым. В моем варианте лучше избегать статики и синглетонов (глобальное состояние) любой ценой. Тестируемый код является наиболее важной частью вашего кода, если вы хотите что-то изменить позже. Тестируемый код обычно также более читабелен.

Родинки и Pex

1 голос
/ 08 ноября 2011

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

1 голос
/ 08 ноября 2011

Статический класс можно смоделировать, хотя, не зная, какой язык / среду вы используете, невозможно сказать, как это сделать.

У меня была кодовая база, которая в точности соответствовала тому, что вы сказали;мы создали интерфейсы и стандартные реализации нескольких десятков статических классов (не паникуйте - генерация кода).Значения по умолчанию только что делегированы синглетам.

Вызывающие классы были переключены на DI с использованием значений по умолчанию, но тогда их было намного проще тестировать.Статические классы с зависимостями от других статических классов или синглетонов, перенесенные с использованием значений по умолчанию.

Вы имитируете синглтон, как и любой другой класс - если у него есть getInstance (или эквивалентный), вы можете вернуть егочто вы хотите.Или вы можете пойти по тому же маршруту и ​​использовать DI.

...