2008-08-10 3 views
8

В последнее время мне пришлось изменить код на старых системах, где не весь код имеет модульные тесты.
Прежде чем вносить изменения, я хочу написать тесты, но каждый класс создал множество зависимостей и других анти-шаблонов, которые сделали тестирование довольно сложным.
Очевидно, что я хотел реорганизовать код, чтобы было легче протестировать, написать тесты и затем изменить его.
Как вы это сделаете? Или вы потратили бы много времени на составление сложных тестов, которые в большинстве случаев будут удалены после завершения рефакторинга?Как вы тестируете/изменяете непроверенный и непроверяемый код?

ответ

5

Прежде всего, here's a great article with tips on unit testing. Во-вторых, я нашел отличный способ избежать тонких изменений в старом коде, чтобы немного переработать его, пока вы не сможете его протестировать. Один простой способ сделать это - защитить частных членов, а затем переопределить защищенное поле.

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

public class MyClass { 
    public MyClass() { 
     // undesirable DB logic 
    } 
} 

становится

public class MyClass { 
    public MyClass() { 
     loadFromDB(); 
    } 

    protected void loadFromDB() { 
     // undesirable DB logic 
    } 
} 

, а затем ваш тест выглядит примерно так:

public class MyClassTest { 
    public void testSomething() { 
     MyClass myClass = new MyClassWrapper(); 
     // test it 
    } 

    private static class MyClassWrapper extends MyClass { 
     @Override 
     protected void loadFromDB() { 
      // some mock logic 
     } 
    } 
} 

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

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

Это немного взломанный, но я нашел его весьма полезным.

0

Я не уверен, почему вы сказали бы, что модульные тесты будут удалены после завершения рефакторинга. Фактически, ваш пакет unit-test должен запускаться после основной сборки (вы можете создать отдельную сборку «тесты», которая просто запускает модульные тесты после создания основного продукта). Затем вы сразу увидите, что изменения в одной части нарушают тесты в другой подсистеме. Обратите внимание, что это немного отличается от выполнения тестов во время сборки (как некоторые могут отстаивать). Некоторое ограниченное тестирование полезно во время сборки, но обычно неэффективно «разбивать» сборку только потому, что некоторые модульные тесты происходят с ошибкой.

Если вы пишете Java (есть вероятность), проверьте http://www.easymock.org/ - может быть полезно для уменьшения сцепления для целей тестирования.

3

@valters

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

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

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

Если проблема слишком часто ломается, это может свидетельствовать о том, что тесты пишутся слишком хрупкими (зависимость от ресурсов, которые могут быть изменены, например, базы данных без использования блока БД должным образом, или внешнюю веб-службу, которую следует издеваться), или это может свидетельствовать о том, что в команде есть разработчики, которые не уделяют должного внимания испытаниям.

Я твердо верю, что неудачный тест должен быть исправлен как можно скорее, так же, как вы исправили код, который не сможет скомпилировать ASAP.

0

Я прочитал «Эффективно работая с устаревшим кодом», и я согласен, что он очень полезен для работы с «неустойчивым» кодом.

Некоторые методы применимы только к скомпилированным языкам (я работаю над «старыми» PHP-приложениями), но я бы сказал, что большая часть книги применима к любому языку.

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

Следует отметить, что я не получил никаких денег от редактора и автора этой книги;), но я нашел это очень интересным, так как ресурсов не хватает в области устаревшего кода (и особенно на моем языке, Французский, но это уже другая история).

Смежные вопросы