Как вы тестируете / изменяете непроверенный и непроверяемый код?

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

10.08.2008 18:49:53
4 ОТВЕТА
РЕШЕНИЕ

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

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

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 в этом случае, но я недавно сделал это в аналогичном случае, потому что я хотел протестировать некоторые функции, совершенно не связанные с загружаемыми данными, так что это было очень эффективно. Я также нашел, что такое разоблачение членов было бы полезно в других подобных случаях, когда мне нужно избавиться от некоторой зависимости, которая была в классе в течение долгого времени.

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

Это что-то вроде хака, но я нашел это весьма полезным.

6
10.08.2008 19:22:36

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

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

0
10.08.2008 20:28:24

@valters

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

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

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

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

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

3
10.08.2008 22:55:03

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

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

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

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

0
30.08.2008 16:44:52