Тестирование перед кодированием - PullRequest
3 голосов
/ 04 декабря 2010

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

Можете ли вы дать мне четкий пример того, как это сделать?

Ответы [ 9 ]

4 голосов
/ 04 декабря 2010

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

По сути, вот идея:

Написание sqrt

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

" My,Я полагаю, что мне понадобится функция, которая принимает число, находит квадратный корень, а затем возвращает это"

Теперь в TDD вместо простого написания кода и проверки выхода чиселверно ... TDD'er говорит:

" Я напишу функцию, которая вызывает мою несуществующую функцию и сравнивает результаты с теми, что у меня в голове "

Итак, он пишет:

void testSqrt () {
    if (sqrt(9.0) == 3.0) {
        printf("ALL IS FINE AND DANDY HERE");
    }
    else {
        printf("THE SYSTEM IS DOWN! THE SYSTEM IS DOWN!"); /* untz untz untz utnz */
    }
}

Теперь его несуществующий sqrt имеет цель в жизни ... убедиться, что он всегда возвращает 3 при кормлении 9!Идея состоит в том, что независимо от того, пишет ли он

float sqrt(float n) {
    long i; float x, y;
    const float f = 1.5;
    x = n/2.0;
    y = n;
    i = *(long *)&y;
    i = 0x5f3759df-(i >> 1);
    y = *(float *) &i;
    y = y*(f-(x*y*y));
    y = y*(f-(x*y*y));
    return n * y;
}

или

float sqrt(float n) {
    float n_t = n/2.0;
    int i = *(int*)&n;
    i = 0x5f375a86 - (i>>1);
    n = *(float*)&i;
    n = n*(1.5f-n_t*n*n);
    return n;
}

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

4 голосов
/ 04 декабря 2010

В последнее время я думал, что этот аспект TDD совпадает с концепцией программирования "желаемое за действительное", о которой я читал в Структура и интерпретация компьютерных программ .Например, в лекции 2A курса MIT, проведенного авторами, приведен следующий пример вычисления квадратных корней как функции фиксированных точек:

(define (sqrt x)
  (fixed-point
      (lambda (y) (average (/ x y) y))
      1))

Это показано до определена процедура fixed-point.Вам не нужно определение, чтобы понять, что вы можете использовать a fixed-point процедуру для вычисления квадратных корней.Как только вы увидите, как вы собираетесь его использовать, вы можете приступить к его определению.

(define (fixed-point f start)
  (define tolerance 0.00001)
  (define (close-enuf? u v)
     (< (abs (- u v)) tolerance))
  (define (iter old new)
     (if (close-enuf? old new)
         new
         (iter new (f new))))
  (iter start (f start)))

Это та же самая простая идея, используемая в TDD.Написав тесты для своих методов перед тем, как писать сами методы, вы показываете, как эти методы должны использоваться.

2 голосов
/ 04 декабря 2010

Прежде чем кодировать что-либо, вы определяете API для него.Затем вы пишете модульный тест для этих API, и все случаи будут неудачными (потому что вы их не реализовали).После того, как большинство случаев подготовлено, у вас есть куча случаев сбоев.После этого вы можете приступить к реализации этих API.По мере того, как вы добиваетесь прогресса, количество неудач должно уменьшиться.Как только все случаи пройдены, ваш дизайн закончен.Это TDD.

1 голос
/ 08 декабря 2010

Чтение Разработка через тестирование: на примере для подробного объяснения.

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

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

0 голосов
/ 08 декабря 2010

Коллега недавно сказал, что первый тест, который он пишет в любом наборе тестов, просто делает:

Assert.Fail("Bootstrapping the test");

, просто чтобы получить его.

0 голосов
/ 04 декабря 2010

Вот пример, который должен работать просто отлично - http://www.theserverside.net/tt/articles/content/TDD_Chapter/ch2.pdf

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

0 голосов
/ 04 декабря 2010

Идея состоит в том, что сначала написание теста:

1) поможет вам разобраться с API
2) поможет вам написать минимальный объем кода.

Допустим, вам нужен метод, который возьмет список и строку и вернет число.вы начинаете с теста, чтобы помочь вам определить, как будет вести себя функция:

public void testMyFunc() {
    List arg1 = new List()...
    String arg2 = 'test';
    int returned;
    int expected = 3;

     // ok what do i need to write
     returned = myFunc(arg1, arg2);

     assertEquals(expected, returned);
}

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

0 голосов
/ 04 декабря 2010
def test_add_numbers
  assert_equal( 42, add(40,2) )
  assert_equal( 42, add(42,0) )
  assert_equal( 42, add(44,-2) )
  assert_equal( 42, add(40,1,1) )
  assert_equal( 42, add(42) )
  assert_raises(ArgumentError,"Add requires one argument"){
    add()
  }
end

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

Вы тестируете интерфейс, который планируете поддерживать, а затем итеративно пишете код и тестируете, пока все ваши тесты не пройдут.

0 голосов
/ 04 декабря 2010

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

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