Чи ідіоматично Ruby додавати метод assert () до класу ядра Ruby?


76

Я розширюю своє розуміння Ruby, кодуючи еквівалент xUnit Кента Бека в Ruby. Python (яким пише Кент) має метод assert () у мові, яка широко використовується. Рубі ні. Я вважаю, що це має бути легко додати, але чи потрібне це ядро?

До речі, я знаю про існування різних фреймворків Unit в Ruby - це вправа вивчати ідіоми Ruby, а не "щось робити".


5
Мені подобаються проблеми, які роблять і те, і інше: навчити вас і щось зробити. :)
Ян Террелл,

Відповіді:


108

Ні, це не найкраща практика. Найкраща аналогія для ствердження () в Ruby - це просто підвищення

 raise "This is wrong" unless expr

і ви можете реалізувати власні винятки, якщо хочете забезпечити більш конкретну обробку винятків


29
Ви також можете використовувати fail if [expr], якщо хочете, що видає помилку виконання.
Xiong Chiamiov

6
failце псевдонім raise, чи не так? Вони обидва дають RuntimeError. (Може, хтось більш ідіоматичний?)
Бенджамін Оукс

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

2
Твердження та винятки - це різні речі.
Джек Кейсі,

6
Твердження @nus не для попередження, вони для забезпечення того, щоб щось завжди відповідало дійсності в цьому місці. Якщо ваше твердження зазнає невдачі у виробництві, ви, безумовно, хочете перервати його.
Ярін,

30

Я вважаю, що абсолютно вірно використовувати твердження в Ruby. Але ви згадуєте дві різні речі:

  • Фреймворки xUnit використовують assertметоди для перевірки очікувань тесту. Вони призначені для використання у вашому тестовому коді, а не в коді вашої програми.
  • Деякі мови, такі як C, Java або Python, включають assertконструкцію, призначену для використання всередині коду ваших програм, щоб перевірити припущення щодо їх цілісності. Ці перевірки будуються всередині самого коду. Вони є не утилітою під час тестування, а розробкою.

Нещодавно я написав solid_assert: маленька бібліотека Ruby, що реалізує утиліту твердження Ruby, а також допис у своєму блозі, що пояснює її мотивацію . Це дозволяє писати вирази у формі:

assert some_string != "some value"
assert clients.empty?, "Isn't the clients list empty?"

invariant "Lists with different sizes?" do
    one_variable = calculate_some_value
    other_variable = calculate_some_other_value
    one_variable > other_variable
end    

І їх можна деактивувати assertі invariantоцінити як порожні оператори. Це дозволить вам уникнути будь-яких проблем із продуктивністю на виробництві. Але зверніть увагу, що Прагматичні програмісти рекомендують не деактивувати їх. Деактивувати їх слід лише в тому випадку, якщо вони дійсно впливають на продуктивність.

Щодо відповіді на те, що ідіоматичний спосіб Рубі використовує звичайне raiseтвердження, я думаю, йому не вистачає виразності. Одне з золотих правил асертивного програмування - не використання тверджень для нормальної обробки винятків. Це дві абсолютно різні речі. Якщо ви використовуєте однаковий синтаксис для них двох, я думаю, що ваш код буде більш неясним. І звичайно, ви втрачаєте можливість їх деактивувати.

Ви можете бути впевнені, що використання тверджень - це добре, тому що дві класичні книги, як «Прагматичний програміст від мандрівника до майстра» та « Код», присвячують їм цілі розділи та рекомендують їх використання. Існує також приємна стаття під назвою Програмування з твердженнями, яка дуже добре ілюструє, що таке асертивне програмування та коли його використовувати (воно базується на Java, але концепції стосуються будь-якої мови).


Я б також запропонував "Написання твердого коду" Стіва Магуайра, який є надзвичайно орієнтованим на C, але розповідає про твердження, стратегію тестування та ідеї побудови функцій, які також стосуються побудови методів.
Пол Кролл,

14

Яка ваша причина для додавання методу утвердження до модуля ядра? Чому б просто не використовувати інший викликаний модуль Assertionsчи щось інше?

Подобається це:

module Assertions
  def assert(param)
    # do something with param
  end

  # define more assertions here
end

Якщо вам дійсно потрібні ваші твердження, щоб вони були доступні скрізь, зробіть щось подібне:

class Object
  include Assertions
end

Застереження: Я не тестував код, але в принципі я б зробив це так.


1
Хіба це мавпа не латає? Ви змінюєте Objectклас.
циков

Так, це класичне латання мавп. Це спосіб це зробити, якщо вам дійсно потрібно зробити assertметод доступним у всьому світі. Однак я б не радив нікому використовувати подібне виправлення мавп у неіграшкових проектах.
Крістоф Шиссль,

8

Це не особливо ідіоматично, але я думаю, що це гарна ідея. Особливо, якщо це зробити так:

def assert(msg=nil)
    if DEBUG
        raise msg || "Assertion failed!" unless yield
    end
end

Таким чином, немає ніякого впливу, якщо ви вирішите не запускати DEBUG (або інший зручний перемикач, я раніше використовував Kernel.do_assert).


5

Я розумію, що ви пишете власний пакет тестувань, щоб ознайомитись із Рубі. Отже, хоча Test :: Unit може бути корисним як керівництво, це, мабуть, не те, що ви шукаєте (адже це вже зроблено).

Тим не менш, твердження python (принаймні для мене) є більш аналогічним твердженню C (3) . Він не спеціально розроблений для модульних тестів, скоріше для охоплення випадків, коли "цього ніколи не повинно статися".

Тоді як вбудовані модульні тести Ruby, як правило, розглядають проблему, полягає в тому, що кожен окремий клас тестового випадку є підкласом TestCase і включає твердження "assert", яке перевіряє достовірність того, що йому було передано, і записує це на звітність.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.