Статьи

Использование преобразований AST для написания библиотеки тестирования

Будучи фанатом языка, я всегда стараюсь написать библиотеку, которая будет работать на языке, о котором я пытаюсь узнать больше. Вы знаете, что-то, что будет активно использовать метапрограммирование или трюки системы типов. Одной из тех библиотек, которые вы можете написать, может быть среда тестирования.

Первое, что я подумал, когда переключился с Groovy на Ruby: «Почему бы не перенести Спока на Ruby?» Для тех, кто не знаком со Spock, это отличная среда тестирования для Groovy. Он использует преобразования AST, что делает возможным такие конструкции как:

when:
  def x = 1
  def y = 1

then:
  x + y == 2

Все в блоке «тогда» является утверждением. Конечно, это может сделать гораздо больше, чем просто вставка утверждений, но это действительно основная особенность Spock, и я решил реализовать нечто подобное в Ruby.

Выбор имени

Не будучи настоящим фанатом Star Trek, я просто выбрал одного персонажа, который мне понравился больше других, и назвал мой проект «Пикард».

Эксперимент, который сделал это намного интереснее

Чтобы было интересно, я решил протестировать «Пикард», используя версию «Пикард». По сути, я довел правило «Ешь свою собачью еду» до крайности. Каждое изменение, которое я делаю, не должно нарушать ничего в системе. Как будто что-то сломано, все мои тесты будут сломаны, и я не могу проверить изменения. Эта безумная идея оказалась очень интересным экспериментом, так как я был вынужден быть очень осторожным со своим кодом. Все итерации были очень маленькими; Мне пришлось написать множество небольших временных адаптеров, чтобы «старая» и «новая» версии кода работали одновременно. Если это не очень весело для вас, просто попробуйте.

в заключение

require 'picard'

class DemoTest < Test::Unit::TestCase
  include Picard::TestUnit

  def test_simple_math
    given
      x = 1
      y = 2

    expect
      x + y == 3
  end
end

Чтобы начать использовать Picard, вам нужно смешать модуль Picard :: TestUnit с тестовым примером TestUnit. Это добавит специальный хук, который преобразует каждый метод теста в вашем тестовом примере. Например, метод «test_simple_math» будет преобразован во что-то вроде:

def test_simple_math
  given
     x = 1
    y = 2

  expect
    assert_equal 3, (x + y), MESSAGE
end

Где СООБЩЕНИЕ:

-----------------------------------------------------------------------------
| File: "/Users/savkin/projects/picard/test/picard/demo_test.rb", Line: 10|
| Failed Assertion: (x + y == 3)                                          |
-----------------------------------------------------------------------------

Вы можете заметить несколько вещей здесь:

  1. Picard использует TestUnit, поэтому со всеми вашими инструментами мы будем работать с ним просто отлично.
  2. Пикард достаточно умен, чтобы вставить assert_equal вместо обычного assert.
  3. Picard генерирует очень описательное сообщение об ошибке, содержащее не только имя файла и номер строки ошибочного утверждения, но и само утверждение. В большинстве случаев достаточно информации, чтобы понять, что пошло не так, поэтому вам не нужно будет искать точный номер строки, чтобы выяснить это.

Я хочу попробовать!

жемчужина «Пикард»

Что будет дальше

Есть некоторые вещи, которые я собираюсь добавить через неделю или две:

  • Единственный особый случай, который сейчас поддерживает Пикард, это ==. Если вы используете что-то вроде x! = Y в ожидаемом блоке, он просто вставит обычный assert, что плохо. Это будет намного умнее, чем это в ближайшее время.

  • В Spock можно писать тесты, управляемые данными:

,

expect:
  x + y == z

where:
  x = [1, 10, 100]
  y = [2, 20, 200]
  z = [3, 30, 300]

В основном это превратит это в нечто вроде:

,

expect:
  1 + 2 == 3
  10 + 20 == 30
  100 + 200 == 300

Что совершенно потрясающе! Я собираюсь добавить аналогичную функцию в Picard в ближайшее время. Теперь Picard написан на Ruby 1.9, но он может анализировать только синтаксис 1.8 (что действительно странно). Его нужно привести в порядок, чтобы он правильно работал на 1.8 и 1.9.

Должен ли я использовать его в производстве

Возможно, нет, но, возможно, в нескольких выпусках, когда он вырастет, вы можете попробовать.

Подводить итоги

На мой взгляд, Пикард является хорошим примером того, чего можно достичь, преобразовав АСТ. Это очень мощная техника, позволяющая изменить семантику языка, недостаточно используемая в сообществе Ruby. Я думаю, что к этому нужно относиться более серьезно. Я хотел бы видеть общую структуру, которая облегчит преобразование AST. Подобный тому, который существует для Groovy.

 

От http://victorsavkin.com/post/11124227221/using-ast-transformations-to-write-a-testing-library