Чи ідіоматично 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.