Чи добре вводити методи, які використовуються лише під час одиничних тестів?


12

Нещодавно я вела TDDing фабричним методом. Метод полягав у створенні або простого предмета, або предмета, загорнутого в декоратор. Оформлений об’єкт може бути одного з декількох типів, що розширює StrategyClass.

У своєму тесті я хотів перевірити, чи клас повернутого об’єкта такий, як очікувалося. Це легко, коли звичайний об'єкт os повернувся, але що робити, коли він загорнутий в декоратор?

Я кодую в PHP, щоб я міг використовувати ext/Reflectionдля пошуку клас загорнутого об'єкта, але мені здалося, що це занадто ускладнює речі, і дещо не відповідає правилам TDD.

Натомість я вирішив ввести, getClassName()що повертає ім'я класу об'єкта, коли його викликають з StrategyClass. Однак, коли його зателефонують від декоратора, він поверне значення, повернене тим самим методом в оформленому об'єкті.

Деякі коди, щоб зробити це більш зрозумілим:

interface StrategyInterface {
  public function getClassName();
}

abstract class StrategyClass implements StrategyInterface {
  public function getClassName() {
    return \get_class($this);
  }
}

abstract class StrategyDecorator implements StrategyInterface {

  private $decorated;

  public function __construct(StrategyClass $decorated) {
    $this->decorated = $decorated;
  }

  public function getClassName() {
    return $this->decorated->getClassName();
  }

}

І тест PHPUnit

 /**
   * @dataProvider providerForTestGetStrategy
   * @param array $arguments
   * @param string $expected
   */
  public function testGetStrategy($arguments, $expected) {

    $this->assertEquals(
      __NAMESPACE__.'\\'.$expected,  
      $this->object->getStrategy($arguments)->getClassName()
    )
  }

//below there's another test to check if proper decorator is being used

Моя думка тут: чи нормально запроваджувати такі методи, які не мають іншого використання, крім того, щоб спростити одиничні тести? Якось мені це не вірно.


Можливо, це запитання дасть більше розуміння вашому питанню, programers.stackexchange.com/questions/231237/… , я вважаю, що це залежить від використання та того, чи допоможуть методи значно допомогти в тестуванні та налагодженні будь-якої програми, яку ви розробляєте .. .
Климент Марк-Ааба

Відповіді:


13

Моя думка - ні, ви нічого не повинні робити тільки тому, що це потрібно для перевірки. Дуже багато рішень, які приймають люди, приносять користь до встановлення і доказовості, можливо, навіть є головною перевагою, але це має бути хорошим дизайнерським рішенням з інших причин. Це означає, що деякі бажані властивості не перевіряються. Інший приклад - коли вам потрібно знати, наскільки ефективною є якась рутина, наприклад, чи ваш Hashmap використовує рівномірно розподілений діапазон хеш-значень - нічого у зовнішньому інтерфейсі не зможе вам це сказати.

Замість того, щоб думати, "чи я отримую правильний стратегічний клас" подумайте ", чи отримує я клас, що виконує те, що намагається перевірити ця специфікація ?" Приємно, коли ви можете протестувати внутрішню сантехніку, але вам не доведеться, просто перевіряйте поворот ручки і бачите, чи потрапляє вам гаряча чи холодна вода.


+1 ОП описує тестування блоку "очистити коробку", а не тестування функцій TDD
Стівен А. Лоу

1
Я бачу шосте епоху, хоча я трохи не бажаю додавати тестування алгоритмів StrategyClass, коли хочу перевірити, чи працює заводський метод. Цей вид порушує ізоляцію ІМХО. Ще одна причина, яку я хочу уникнути, полягає в тому, що ці конкретні класи працюють на базі даних, тому для їх тестування потрібні додаткові глузування / заглушки.
Mchl

З іншого боку, і з огляду на це питання: programmers.stackexchange.com/questions/86656/…, коли ми відрізняємо "тести TDD" від "одиничних тестів", це стає ідеально добре (все ж
базова

Якщо ви додасте методи, вони стають частиною договору з вашими користувачами. Ви отримаєте кодери, які зателефонують лише на тестові функції та відділення на основі результатів. Як правило, я вважаю за краще виставляти якомога менше класу.
BillThor

5

Моя думка про це - іноді вам доведеться трохи переробити свій вихідний код, щоб зробити його більш зрозумілим. Це не ідеально і не повинно бути приводом, щоб захаращувати інтерфейс функціями, які повинні використовуватись лише для тестування, тому модерація, як правило, тут є ключовою. Ви також не хочете опинитися в ситуації, коли користувачі вашого коду раптом використовують функції тестового інтерфейсу для нормальної взаємодії з вашим об'єктом.

Мій кращий спосіб впоратися з цим (і я повинен вибачитися, що я не можу показати, як це зробити в PHP, оскільки я в основному кодую на мовах стилю C) - це надання "тестових" функцій таким чином, щоб вони не були піддаються впливу навколишнього світу самим об'єктом, але можуть отримати доступ до похідних об'єктів. Для цілей тестування я отримав би клас, який би обробляв взаємодію з об'єктом, який я насправді хочу протестувати, а тестовий блок використовує саме цей об'єкт. Приклад C ++ виглядатиме приблизно так:

Тип виробництва:

class ProdObj
{
  ...
  protected:
     virtual bool checkCertainState();
};

Тест помічників класу:

class TestHelperProdObj : public ProdObj
{
  ...
  public:
     virtual bool checkCertainState() { return ProdObj::checkCertainState(); }
};

Таким чином, ви, принаймні, знаходяться в положенні, коли вам не потрібно виставляти функції "тестування" у вашому головному об'єкті.


Цікавий підхід. Мені потрібно було б побачити, як я міг би це адаптувати
Mchl

3

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

Тестування важливо, якщо потрібно, просто додайте щось для цього.

Але спробуйте деякі з альтернатив. Ваш варіант на основі роздумів не все так погано. Ви можете мати захищений віртуальний доступ до того, що вам потрібно, і створити похідний клас для перевірки та затвердження. Можливо, ви можете розділити свій клас і безпосередньо протестувати отриманий клас листя. Приховування методу тестування зі змінною компілятора у вихідному коді також є варіантом (я ледве знаю PHP, не впевнений, чи можливо це в PHP).

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


1

Я абсолютно новачок TDD, але, схоже, це залежить від методу, який додається. З того, що я розумію в TDD, ваші тести повинні певною мірою "керувати" створенням вашого API.

Коли це добре:

  • Поки метод не порушує інкапсуляцію і не виконує мету, що відповідає відповідальності об'єкта.

Коли це не в порядку:

  • Якщо метод здається чимось, що ніколи не буде корисним, або не має сенсу стосовно інших методів інтерфейсу, він може бути згуртованим або заплутаним. У той момент це морочило моє розуміння цього об’єкта.
  • Приклад Джеремі, "... коли вам потрібно знати, наскільки ефективною є якась рутина, наприклад, чи ваш Hashmap використовує рівномірно розподілений діапазон хеш-значень - нічого у зовнішньому інтерфейсі не зможе вам це сказати".
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.