2010-08-30 3 views
1

Давайте предположим, что мы разрабатываем класс Stack тест-первый (TDD):Пара вопросов о модульном тестирования

public class Stack<T> { 
    private T[] elements = new T[16]; 
    private int size = 0; 
    ... 
} 

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

я первый выбрал что-то в виде:

Should_Be_Able_To_Correctly_Increase_Its_Inner_Array_Size() 

, а затем

Should_Handle_More_Items_Than_The_Default_Internal_Array_Size() 

, но подумав немного, я пришел к выводу, что, возможно, что-то вроде следующего будет более apropriate :

Should_Double_Its_Size_Every_Time_Its_Full() 

Мое рассуждение должно было бы сделать это в первом случае, я говорю только то, что он делает, но не тогда. Во-вторых, я говорю, когда добавлять больше предметов, но я также заявляю, как я думаю о его достижении (внутренне), что может быть неверным. По моему мнению (я не уверен, что я прав), мои тесты должны включать возможные взаимодействия моего SUT с внешним видом, а не о том, как он реализован внутри страны. Я прав?

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

Который приводит меня к моему второй вопрос: Предположим, что я сделал все юнит-тестов для моего класса Stack, который использует внутри массива, и он прекрасно работает как и ожидалось, должны мои тесты остаются нетронутыми, если позже я хочу, чтобы реорганизовать и изменить Array на ArrayList или любую другую структуру данных? Или тесты должны каким-либо образом отражать это? Думаю, нет, но я не уверен.

+0

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

ответ

3

На мой взгляд (я не уверен, что я правильно ), мои тесты должны возможные взаимодействия моей SUT с внешности, а не о том, как она реализуется внутри. Я прав?

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

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

2

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

Ответ на ваш второй вопрос: да, тест должен пройти, если это единичный тест.

+0

При выполнении этого класса TDD-стиля, какие тесты я бы делал, тогда? –

+0

Проверьте внешний интерфейс и убедитесь, что он работает должным образом. –

1

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

2

Задайте себе, что вы готовы совершить для этого класса.

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

public Stack<T>(CapacityGrowthStyle capacityGrowthStyle) { ... } 

Если нет, просто писать тесты, чтобы документировать емкость и оставить его в том, что (где ниже X ваш исходные данные предельная структура в):

[Test] 
public void Can_Handle_X_Items() { ... } 

[Test] 
[ExpectedException(typeof(InvalidOperationException))] 
public void Cannot_Handle_More_Than_X_Items() { ... } 

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

+0

Я получаю ваши очки. Теперь, если я следую указаниям TDD, что мне делать, чтобы добавить этот «бит функциональности»? Теперь, когда я это вижу, это мой главный вопрос! –

+1

@devoured - это интересный (и сложный) вопрос. Если бы алгоритм увеличения емкости действительно имел значение для моих пользователей (предположительно по причинам производительности), у меня возникло бы желание добавить свойство «Capacity» для облегчения тестирования, хотя пользователям, вероятно, обычно не понадобилось бы это свойство. Но я бы пожалел о загромождении API. :( –

+0

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

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