Тестирование абстракных классов

denver

?>Скриптер
Тестирование абстракных классов

Есть абстракный класс со своей функциональностью. Его наследуют остальные классы. Как протестировать этот абстракный класс? Создать экземпляр напрямую невозможно, а тестировать его функциональность в каждом наследуемом это вроде как преступление против TDD (повтор кода).

А что делать? Не использовать директиву abstract?
Создать фиктивного потомка и чтобы юзать его только для теста?

А это ли не преступления?
 

denver

?>Скриптер
угу. получается что так. А если скажем мне нужно протестировать две-три разные ситуации (ну например есть какие-нибудь три критичные варианта работы, зависит от потомка) то придется создавать и два-три потомка... Грустно :(
 

ТопольМ

Новичок со стажем
грустно, но делать нечего :)
тестирование - неблагодарное занятие, но без него никуда
 

whirlwind

TDD infected, paranoid
>ну например есть какие-нибудь три критичные варианта работы, зависит от потомка

Эти методы делаются абстрактными и как и положено тестируются в соответствующем юните
 

denver

?>Скриптер
Ну вот например:

PHP:
abstract class Page {
  protected $html;

  abstract function prepare();
  abstract function insertdata();
  abstract function finalize();

  function getHTML() {
    $this->prepare();
    $this->insertdata();
    $this->finalize();
    return $this->html;
  }

}
Все функции могут делать что-то с $this->html, должны вызываться в именно таком порядке. Проверить сразу всё, создав класс со всеми непустыми функциями и сравнив getHTML() с предполагаемым результатом -- выходит большой тест. Нужно ведь разбивать его на проверки:
1) prepare() отрабатывает
2) initialize() отрабатывает
3) finalize() отрабатывает
4) все запускаются в нужном порядке
Если я праввыильно понимаю в такой ситуации нужно иметь 4 класса: в первых 3х только один метод не пустой и вставляет что-то в $this->html, а в 4-ом все не пустые.
Или я неправильно понимаю?
 

whirlwind

TDD infected, paranoid
да там теста то 3 копейки

PHP:
class PageTestClass extends Page {
   function prepare(){ $this->html .= "a"; }
   function insertdata(){ $this->html .= "b"; }
   function finalize(){ $this->html .= "c"; }
}

class PageTest extends UnitTestCase {
   function testSimply(){
      $t = new PageTestClass;
      $this->assertEqual( $t->getHTML(), "abc" );
   }
}
Все. После этого считаем, что единственный мето Page работает правильно.

В более сложных случаях, расставляем в тестовом классе счетчики на вызовы. А в кейсе считаем.
 

denver

?>Скриптер
whirlwind
Ок, спасибо. Я просто стараюсь понять какое приемлемое решение. Если это в порядке вещей то не буду загоняться.

Если можно опишите плиз в двух словах что за счётчики на вызовы или где почитать об этом?
 

whirlwind

TDD infected, paranoid
Ну вообще Моки (MockObjects), но лично я их в таком виде не использую. Про моки есть на этом сайте и на http://agiledev.ru

А счетчики спасают самые элементарные
PHP:
class PageTestClass extends Page { 
   public $prepareCalls = 0;
   public $insertdataCalls = 0;
   public $finalizeCalls = 0;

   function prepare(){ $this->prepareCalls ++; } 
   function insertdata(){ $this->insertdataCalls ++; } 
   function finalize(){ $this->finalizeCalls ++; } 
}

class PageTest extends UnitTestCase { 
   function testSimply(){ 
      $t = new PageTestClass;
      $t->getHTML(); 
      $this->assertEqual( $t->prepareCalls,1 ); 
      $this->assertEqual( $t->insertdataCalls,1 ); 
      $this->assertEqual( $t->finalizeCalls,1 ); 
   }
Не надо думать что SimpleTest говорит - "делать только так" и "я все могу". TDD это технология для вас, а не наоборот. Сильно заморачиваться над тестами - терять время. Пишите как думаете, с практикой кривизна рук исчезнет.

-~{}~ 30.06.06 19:37:

ЗЫ. вообще по поводу счетчиков, наверное вместо моков лучше использовать декораторы. В PHP есть все средства, что бы декорировать любой класс автоматически.
 
Сверху