Модульное тестирование большого метода - PullRequest
6 голосов
/ 25 октября 2009

После разработки через тестирование.

Недавно я реализовал алгоритм (A *), который требовал чистого интерфейса. По чистой все, что я хочу, это пара свойств и один метод поиска.

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

Есть какой-нибудь совет для этого?

Редактировать

Я использую C #. Нет, у меня нет кода под рукой в ​​данный момент. Моя проблема заключается в том, что тест проходит только после реализации всего метода поиска, а не шага в алгоритме. После этого я, естественно, реорганизовал код, но его реализация мне показалась сложной.

Ответы [ 5 ]

11 голосов
/ 25 октября 2009

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

Не знаю, какой язык вы используете, но если вы находитесь в мире .net, вы можете сделать эти классы внутренними, а затем представить их вашему тестовому классу с "внутренними элементами, видимыми для", которые будут скрывать их.

Если шаги небольшие и бессмысленные сами по себе, то предложения tvanfosson - это путь.

4 голосов
/ 25 октября 2009

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

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

1 голос
/ 25 октября 2009

При использовании TDD важно помнить, что тестам не нужно жить вечно.

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

Мой совет для этого сценария:

  • Нарисуйте скелет большого метода с точки зрения новых методов, так что у вас есть ряд шагов, которые имеет смысл проверить индивидуально.
  • Сделайте эти новые методы общедоступными по умолчанию и начните разрабатывать их с использованием TDD.
  • Затем, как только вы протестируете все детали, напишите другую, которая тестирует метод whole . Гарантируя, что если какая-либо из меньших делегированных частей будет повреждена, этот новый тест не пройдёт.
  • На данный момент большой метод работает и покрыт тестами, теперь вы можете приступить к рефакторингу. Я бы порекомендовал, как FinnNk , попытаться извлечь меньшие методы в собственные классы. Если это не имеет смысла, или если это займет слишком много времени, я бы порекомендовал что-то, с чем другие могут не согласиться: измените маленькие методы на частные и удалите тесты для них.

В результате вы использовали TDD для создания всего метода, метод все еще рассматривается в тестах, и API - это именно тот, который вы хотите представить.

Большая путаница возникает из-за идеи, что после того, как вы написали тест, вы всегда должны его держать , и это не всегда так. Однако, если вы сентиментальны, существуют способы, которыми можно выполнить модульные тесты для закрытых методов в Java , возможно, есть аналогичные конструкции в C #.

1 голос
/ 25 октября 2009

Я предполагаю, что A * означает алгоритм поиска (например, http://en.wikipedia.org/wiki/A*_search_algorithm).. Если это так, я понимаю вашу проблему, поскольку у нас аналогичные требования. Вот алгоритм WP, и я прокомментирую ниже:

<ч /> Псевдокод

 function A*(start,goal)
     closedset := the empty set                 % The set of nodes already evaluated.     
     openset := set containing the initial node % The set of tentative nodes to be evaluated.
     g_score[start] := 0                        % Distance from start along optimal path.
     h_score[start] := heuristic_estimate_of_distance(start, goal)
     f_score[start] := h_score[start]           % Estimated total distance from start to goal through y.
     while openset is not empty
         x := the node in openset having the lowest f_score[] value
         if x = goal
             return reconstruct_path(came_from,goal)
         remove x from openset
         add x to closedset
         foreach y in neighbor_nodes(x)
             if y in closedset
                 continue
             tentative_g_score := g_score[x] + dist_between(x,y)

             if y not in openset
                 add y to openset

                 tentative_is_better := true
             elseif tentative_g_score < g_score[y]
                 tentative_is_better := true
             else
                 tentative_is_better := false
             if tentative_is_better = true
                 came_from[y] := x
                 g_score[y] := tentative_g_score
                 h_score[y] := heuristic_estimate_of_distance(y, goal)
                 f_score[y] := g_score[y] + h_score[y]
     return failure

 function reconstruct_path(came_from,current_node)
     if came_from[current_node] is set
         p = reconstruct_path(came_from,came_from[current_node])
         return (p + current_node)
     else
         return the empty path

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

<Ч />

Во-первых, и я не легкомысленен, это зависит от того, понимаете ли вы алгоритм - это звучит так, как будто вы понимаете. Также можно было бы расшифровать вышеприведенный алгоритм - надеясь, что он сработает) и провести ряд тестов. Вот что я бы сделал, так как подозреваю, что авторы WP лучше меня! Крупномасштабные тесты выполняли бы граничные случаи, такие как отсутствие узла, один узел, два узла + отсутствие ребра и т. Д. Если бы все они прошли, я бы заснул счастливым. Но если они потерпели неудачу, нет другого выбора, кроме как понять алгоритм.

Если это так, я думаю, вы должны построить тесты для структур данных. Это (как минимум) набор, расстояние, оценка и т. Д. Вы должны создать эти объекты и проверить их. Каково ожидаемое расстояние для случая 1,2,3 ... написать тесты. Каков эффект добавления A для установки Z? нужен тест. Для этого алгоритма вам нужно проверить heuristic_estimate_of_distance и так далее. Это много работы.

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

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

0 голосов
/ 25 октября 2009

Вы можете рассмотреть Texttest (http://texttest.carmen.se/) как способ тестирования "под интерфейсом".

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

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: Я слышал презентацию на Texttest и смотрел документы, но еще не успел опробовать ее в серьезном приложении.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...