미니테스트

AGAIN 2011 RAILS WORKSHOP

2016-07-24

미니테스트 개요

  • 테스트 프레임워크가 필요한 이유
    • LESS Bugs vs MORE Lines of Code
  • Test-First 테스트 코드부터 작성하는 습관
  • RED, GREEN, REFACTOR
    • red 코드를 작성하기 전에 테스트 코드 실행하면 실패
    • green 테스트 코드가 통과하도록 실제 코드를 작성함.
    • refactor 작성한 코드를 개선
  • 부정적 이미지 Over-engineering

테스트에 대한 의견 참고

미니테스트 장점

  • 미니테스트 장점

  • 알스펙보다 빠르고 가볍다.

    참고: 알스펙(RSpec)은 2015년 5월까지 1위 (ruby-toolbox) 그 이후 2016년 7월 현재 미니테스트 1위 
  • 이해하기 쉽고 확장하기 좋다.

  • 순서와 무관하게 실행할 수 있고 동시에 여러개를 실행할 수 있다.

  • 루비 1.9+ 테스트 유닛을 대체함

미니테스트 사례

# string_digit_test.rb
require 'minitest/autorun'
require 'string_digit' 
# ruby -I . string_digit_test.rb
class StringDigitTest < MiniTest::Unit::TestCase
  def test_is_digit
    assert "5".is_digit?
  end

  def test_is_not_digit
    assert !"five".is_digit?
  end
end
# string_digit.rb
class String
  # FIRST CODE
  def is_digit?
    if self =~ /^\d+$/
      true
    else
      false
    end
  end

  # REFACTOR
  # def is_digit?
  #  self =~ /^\d+$/
  # end
end
MiniTest::Unit::TestCase is now Minitest::Test. From string_digit_test.rb:4:in `<main>'
Run options: --seed 4697

# Running:

EE

Finished in 0.001373s, 1456.7863 runs/s, 0.0000 assertions/s.

  1) Error:
StringDigitTest#test_is_not_digit:
NoMethodError: undefined method `is_digit?' for "five":String
    string_digit_test.rb:10:in `test_is_not_digit'


  2) Error:
StringDigitTest#test_is_digit:
NoMethodError: undefined method `is_digit?' for "5":String
    string_digit_test.rb:6:in `test_is_digit'

2 runs, 0 assertions, 0 failures, 2 errors, 0 skips
Run options: --seed 23319

# Running:

..

Finished in 0.001428s, 1400.2582 runs/s, 1400.2582 assertions/s.

2 runs, 2 assertions, 0 failures, 0 errors, 0 skips

assertions

  • assert
  • assert_block
  • assert_empty
  • assert_equal
  • assert_in_delta
  • assert_in_epsilon
  • assert_includes
  • assert_instance_of
  • assert_kind_of

 

  • assert_match
  • assert_nil
  • assert_operator
  • assert_output
  • assert_raises
  • assert_respond_to
  • assert_same
  • assert_send
  • assert_silent

assertions

  • assert <condtional>
  • assert_equal <expected>, <actual>
  • assert_not_equal
  • assert_respond_to <object>, :method
  • assert_nil <object>
  • assert_match <pattern>, <string>
  • assert_raise(<error>) { <code> }
  • assert_kind_of(<Class>, <object>)

spec-style

describe Something do
  subject { Something.new(name: 'Widget') }
  context 'something else' do
    it 'does something' do
      subject.name.must_equal 'Widget'
    end
  end
end
class MiniTest::Spec
  class << self
    alias :context, :describe
  end
end
describe Something do
  it 'does something' 
  it 'does something else' do
    skip
  end
  it 'does another thing' do
    skip 'here's some reason why'
  end
end
describe Something do
  it 'does something' do
    lambda {nil / 2}.must_raise NoMethodError
    ["one", "two"].must_include "one"
    ["one", "two"].wont_include "three"
    nil.must_be_nil
    [].must_be :empty?
  end
end

expectations

  • must_be
  • must_be_close_to
  • must_be_empty

  • must_be_instance_of

  • must_be_kind_of

  • must_be_nil

  • must_be_same_as

  • must_be_within_epsilon

     

  • must_be
  • must_equal

  • must_include

  • must_match

  • must_output

  • must_respond_to

  • must_raise

  • must_send

  • must_throw

must_ <=> wont_

custom assertion

module MiniTst::Assertions
  def assert_equals_rounded(rounded, decimal)
    assert rounded == decimal.round, "Expected #{decimal} to round to #{rounded}"
  end
  def refute_equals_rounded(rounded, decimal)
    assert rounded != decimal.round, "Expected #{decimal} to not round to #{rounded}"
  end
end

Numeric.infect_an_assertion :assert_equals_rounded, :must_round_to
Numeric.infect_an_assertion :refute_equals_rounded, :wont_round_to


decribe Something do
  it "does something" do
    1.2.must_round_to 1
    1.7.wont_round_to 1
  end
end

rails testing with minitest

#Gemfile
group :test do
  gem 'minitest'
  gem 'capybara'
end
 
#test/minitest_helper.rb
ENV["RAILS_ENV"] = "test"
require File.expand_path("../../config/environment", __FILE__)
require "minitest/autorun"
require "capybara/rails"
require "active_support/testing/setup_and_teardown"

class IntegrationTest < MiniTest::Spec
  include Rails.application.routes.url_helpers
  include Capybara::DSL
  register_spec_type(/integration$/, self)
end

class HelperTest < MiniTest::Spec
  include ActiveSupport::Testing::SetupAndTeardown
  include ActionView::TestCase::Behavior
  register_spec_type(/Helper$/, self)
end
#lib/tasks/minitest.rake
require "rake/testtask"

Rake::TestTask.new(:test => "db:test:prepare") do |t|
  t.libs << "test"
  t.pattern = "test/**/*_test.rb"
end

task :default => :test
rails new store -T
rails g scaffold product name price:decimal released_on:date
#Gemfile
group :test do
  minitest-rails
  minitest-rails-capybara
end
 

rails testing with minitest

/test/model/product_test.rb
require "minitest_helper"

class ProductTest < MiniTest::Unit::TestCase
  def test_to_param
    product = Product.create!(name: "Hello World")
    assert_equal "#{product.id}-hello-world", product.to_param
  end
end
#test/integration/products_integration_test.rb
require "minitest_helper"

describe "Products integration" do
  it "shows product's name" do
    product = Product.create!(name: "Tofu")
    visit product_path(product)
    page.text.must_include "Tofu"
  end
end
#test/helpers/product_helper_test.rb
require "minitest_helper"

describe ProductsHelper do
  it "converts number to currency" do
    number_to_currency(5).must_equal "$5.00"
  end
end

railscast https://www.youtube.com/watch?v=T3rCJftb7aY

참고 자료

https://www.facebook.com/seongjun.p.kim/posts/1058363404212697

 

Minitest Insights with Ryan Davis https://vimeo.com/75833835

RailsConf 2015 - Ruby on Rails on Minitest

https://www.youtube.com/watch?v=MA4jJNUG_dI

How to Use MiniTest

http://blog.teamtreehouse.com/short-introduction-minitest

railscast how i test

https://www.youtube.com/watch?v=AQ-Vf157Ju8

 

감사합니다.

Welcome to minitest

By wagurano

Welcome to minitest

again2011 rails workshop

  • 1,423