Что сегодня узнаем?
- Зачем нужны тесты?
- Как тестировать код в Ruby?
- Структура тестов
- Вспомогательные инструменты
Зачем тестировать?
- Убедиться, что новый функционал не ломает старый
- Выявить баги на этапе деплоя, а не в проде
- Для согласованной работы команды разработчиков
- Выявить неправильную архитектуру
Виды тестирования
- Блочное
- Интеграционное
- Системное
Структура теста
- Начальные условия
- Ожидаемый результат
- Проверка результата
Minitest
Пример теста на Minitest:
require 'minitest/autorun'
def greeting(name)
  "Hello, #{name}!"
end
class MyTest < Minitest::Test
  def setup; end
  def teardown; end
  def test_if_dummy_method_returns_true
    name = 'Bob'
    expected_greeting = 'Hello, Bob!'
    assert(greeting(name) == expected_greeting)
  end
end
class MyTest < Minitest::Test
  def test_one
    assert true
  end
  def test_two
    refute false
  end
  def test_three
    refute false
    assert false
  end
  def test_flaky
    assert [false, true].sample
  end
  def test_for_procrastinators
    skip "i'll do it later"
  end
end
Assertions
describe "Assertions" do
  it 'should be true' do
    var = true
    assert var
  end
  it 'should be false' do
    var = false
    refute var
  end
  it 'should be equal' do
    var = 2
    assert_equal var, 2
  end
  it 'should be nil' do
    assert_nil nil
  end
  it 'should be empty' do
    assert_empty []
    assert_empty ''
    assert_empty({})
  end
  it 'matches regexp' do
    assert_match(/\w+/, "some string")
  end
  it 'should include value' do
    assert_includes(0..10, 10)
  end
  it 'should throw a symbol' do
    assert_throws :symbol do
      return :symbol
    end
  end
  it 'should be instance of' do
    assert_instance_of Array, []
  end
  it 'should be descendant of Enumerable' do
    assert_kind_of Enumerable, Array.new
    assert_kind_of Enumerable, Range.new(0, 10)
    assert_kind_of Enumerable, Hash.new
  end
  it 'should be descendant of Enumerable' do
    [Array.new, Range.new(0, 10), Hash.new].each do |instance|
      assert_kind_of Enumerable, instance
    end
  end
end
Expectation
describe "Expection" do
  it 'should be true' do
    var = true
    _(var).must_equal true
  end
  it 'should be false' do
    var = false
    _(var).must_equal false
  end
  it 'should be equal' do
    var = 2
    _(var).must_equal 2
  end
  it 'should be nil' do
    _(nil).must_be_nil
  end
  it 'should be empty' do
    _([]).must_be_empty
    _('').must_be_empty
    _({}).must_be_empty
  end
  it 'matches regexp' do
    _("some string").must_match(/\w+/)
  end
  it 'should include value' do
    _(0..10).must_include 10
    _(0...10).wont_include 10
  end
  it 'should throw a symbol' do
    _ { return :symbol }.must_throw :symbol
  end
  it 'should not be instance of' do
    _([]).wont_be_instance_of Hash
  end
  it 'should not be descendant of Comparable' do
    [Array.new, Range.new(0, 10), Hash.new].each do |instance|
      _(instance).wont_be_kind_of(Comparable)
    end
  end
end
Хуки
class MyTest < Minitest::Test
  def before_setup
    puts 'a'
  end
  def setup
    puts 'b'
  end
  def after_setup
    puts 'c'
  end
  def before_teardown
    puts 'd'
  end
  def teardown
    puts 'e'
  end
  def after_teardown
    puts 'f'
  end
  def test_assert
    assert true
  end
end
describe "My Test" do
  before do
    puts '1'
  end
  before :setup do
    puts '2'
  end
  after do
    puts '3'
  end
  it 'test' do
    assert true
  end
end
Запуск тестов
## Обычный запуск тестов
ruby example_test.rb
## Запуск с детальным логирование выполнения
ruby example_test.rb -v
## Запуск с паттерном названия метода
ruby example_test.rb -n test_one
## Запуск с исключающим паттерном названия метода
ruby example_test.rb -e test_one
## Запуск с детерминированным сидом для псевдослучайного генератора
ruby example_test.rb -s 10000
Самостоятельная работа
- Создайте репозиторий ruby-testing. 
- Добавьте в Gemfile гемы - minitest,- minitest-power_assertв Gemfile и установите его:- gem install 'minitest-power_assert'
- Напишите тесты для функции нахождения суммы четных чисел - # sum_even.rb
def sum_even(numbers) raise ArgumentError, 'Input must be an array' unless numbers.is_a?(Array) numbers.select { |n| n.is_a?(Integer) && n.even? }.sum end
sum_even([1, 2, 3, 4, 5, 6]) # 2 + 4 + 6 = 12 sum_even([1, 3, 5]) # Нет четных чисел sum_even([1, 2, 3, 4, 5, 6, 8]) # 2 + 4 + 6 + 8 = 20 sum_even([1, 2, 'three', 4, nil, 6]) # 2 + 4 + 6 = 12
Дополнительные материалы
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.