private double size;

public Shape(int shapeType, double size) {

   this.shapeType = shapeType;

   this.size = size;

}

//… другие методы…

public double area() {

 switch (shapeType) {

  case SQUARE: return size*size;

  case CIRCLE: return Math.PI*size*size/4.0;

  case RIGHT TRIANGLE: return size*size/2.0;

}

return 0;

}

40. Данная программа на языке Java представляет собой часть некоего скелета, который будет использоваться во всем вашем проекте. Произведите реорганизацию этой программы, чтобы сделать ее более общей и упростить ее расширение в будущем. (Ответ см. в Приложении В.)

public class Window {

  public Window(int width, int height) {…}

  public void setSize(int width, int height) {…}

  public boolean overiaps(Window w) {…}

  public int getArea() {…}

<p>34</p><p>Программа, которую легко тестировать</p>

Термин «программная интегральная схема» является метафорой, брошенной в ходе дискуссии о многократном использовании и компонентно-ориентированной разработке [39]. Идея заключается в том, что программные компоненты должны объединяться так же, как это происходит с чипами интегральной схемы. Этот подход срабатывает только в том случае, если известно, что используемые компоненты надежны.

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

То же самое можно осуществить и с программным обеспечением. Подобно нашим коллегам, работающим с «железом», нам приходится с самого начала встраивать средства тестирования в программы и тщательно тестировать каждый фрагмент, перед тем как предпринять попытку их объединения.

<p>Модульное тестирование</p>

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

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

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

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

<p>Тестирование в рамках контракта</p>

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

Что это означает на практике? Рассмотрим подпрограмму извлечения квадратного корня, с которой мы впервые встретились в разделе «ППК и аварийное завершение работы программы». Ее контракт довольно прост:

require:

   argument >=0

ensure:

  abs((result*result)–argument) < epsilon

Он указывает на моменты, нуждающиеся в проверке:

• Передать отрицательный аргумент и удостовериться в том, что он отклонен

• Передать аргумент, равный нулю, и удостовериться в том, что он принят (это граничное значение)

• Передать значение в интервале от нуля до максимально выражаемого параметра и проверить, что разность между квадратом результата и исходным аргументом меньше некоторой величины «epsilon»

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

public void testValue(double num, double expected) {

double result = 0.0;

try {    // We may throw a

result = mySqrt(num); // precondition exception

}

catch (Throwable e) {

if (num<0.0) // If input is «0, then

Перейти на страницу:

Все книги серии Библиотека программиста

Похожие книги