Qiitaとrubyとtest/unitとリファクタリング初心者です!!
タイトル長いな...
リファクタリング:Rubyエディション読んで、サンプルコードを見つけて、
リファクタリング:Rubyエディションの写経を読んでて、開始時のサンプルコードとテストフレームワークのコードが見当たらなかったので書いた。テストコードは初めて書くから結構強引に書いた。
rspecも人気だけど、今回はテストはruby標準ライブラリのtest/unitで書いた。
コメントアウトは適宜外して動かしてほしい。
リファクタリング後のコードに差し替えると微妙に動かないところがあるので適宜書き換えが必要かも。html表示のテストは書いてない。
リファクタリングの経緯と変更点も追記するかもしれないし、しないかもしれない。
2017/08/27追記
ruby2.0以降requireがカレントディレクトリ明示して./testじゃないと動かないところを修正
minitest版も追記。minitestは$ sudo gem install minitestでインストールできる。minitestのほうがtest/unitより便利っぽい
result = "Rental Record for #{@name}\n "の文末の謎のスペースを除去。ここでtest通らなかった。正しくは result = "Rental Record for #{@name}\n"。
# test.rb
class Movie
	REGULAR = 0
	NEW_RELEASE = 1
	CHILDRENS = 2
	attr_reader :title
	attr_accessor :price_code
	def initialize(title, price_code)
		@title, @price_code = title, price_code
	end
end
class Rental
	attr_reader :movie, :days_rented
	def initialize(movie, days_rented)
		@movie, @days_rented = movie, days_rented
	end
end
class Customer
	attr_reader :name
	def initialize(name)
		@name = name
		@rentals = []
	end
	def add_rental(arg)
		@rentals << arg
	end
	def statement
 	total_amount, frequent_renter_points = 0, 0
	result = "Rental Record for #{@name}\n"
	@rentals.each do |element|
	this_amount = 0
	# 各行の金額を計算
	case element.movie.price_code
	when Movie::REGULAR
		this_amount += 2
		this_amount += (element.days_rented - 2) * 1.5 if element.days_rented > 2
	when Movie::NEW_RELEASE
		this_amount += element.days_rented * 3
	when Movie::CHILDRENS
		this_amount += 1.5
		this_amount += (element.days_rented - 3) * 1.5 if element.days_rented > 3
	end
	# レンタルポイントを加算
	frequent_renter_points += 1
	# 新作2日間レンタルでボーナス点を加算
	if element.movie.price_code == Movie::NEW_RELEASE && element.days_rented > 1
		frequent_renter_points += 1
	end
	# このレンタルの料金を表示
	result += "\t" + element.movie.title + "\t" + this_amount.to_s + "\n"
	total_amount += this_amount
	end
	# フッター行を追加
	result += "Amount owed is #{total_amount}\n"
	result += "You earned #{frequent_renter_points} frequent renter points"
	result
	end
end
# リファクタリング後
=begin
# encodling:utf-8
# ----- Movieクラス ------
class Movie
  REGULAR = 0
  NEW_RELEASE = 1
  CHILDRENS = 2
  attr_reader :title
  attr_reader :price_code
  def price_code=(value)
    @price_code = value
    @price = case price_code
    when REGULAR; RegularPrice.new
    when NEW_RELEASE; NewReleasePrice.new
    when CHILDRENS; ChildrensPrice.new
    end
  end
  def initialize(title, the_price_code)
    @title, self.price_code = title, the_price_code
  end
  def charge(days_rented)
    @price.charge(days_rented)
  end
  def frequent_renter_points(days_rented)
    @price.frequent_renter_points(days_rented)
  end
end
# ----- DefaultPriceモジュール -----
module DefaultPrice
  def frequent_renter_points(days_rented)
    1
  end
end
# ----- RegularPriceクラス -----
class RegularPrice
  include DefaultPrice
  def charge(days_rented)
    result = 2
    result += (days_rented - 2) * 1.5 if days_rented > 2
    result
  end
end
# ----- NewReleasePriceクラス -----
class NewReleasePrice
  def charge(days_rented)
    days_rented * 3
  end
  def frequent_renter_points(days_rented)
    days_rented > 1 ? 2 : 1
  end
end
# ----- ChildrensPriceクラス -----
class ChildrensPrice
  include DefaultPrice
  def charge(days_rented)
    result = 1.5
    result += (days_rented - 3) * 1.5 if days_rented > 3
    result
  end
end
# ----- Rentalクラス -----
class Rental
  attr_reader :movie, :days_rented
  def initialize(movie, days_rented)
    @movie, @days_rented = movie, days_rented
  end
  # 金額を計算
  def charge
    movie.charge(days_rented)
  end
  def frequent_renter_points
    movie.frequent_renter_points(days_rented)
  end
end
# ----- Customerクラス -----
class Customer
  attr_reader :name
  def initialize(name)
    @name = name
    @rentals = []
  end
  def add_rental(arg)
    @rentals << arg
  end
  def statement
    result = "Rental Record for #{@name}\n"
    @rentals.each do |element|
      # このレンタルの料金を表示
      result += "\t" + element.movie.title + "\t" + element.charge.to_s + "\n"
    end
    # フッター行を追加
    result += "Amount owed is #{total_charge}\n"
    result += "You earned #{total_frequent_renter_points} frequent renter points"
    result
  end
  def html_statement
    result = "<h1>Rentals for <em>#{@name}</em></h1><p>\n"
    @rentals.each do |element|
      result += "\t" + element.movie.title + ": " + element.charge.to_s + "<br>\n"
    end
    result += "<p>You owe <em>#{total_charge}</em><p>\n"
    result += "On this rental you earned" + "<em>#{total_frequent_renter_points}</em> " + "frequent renter points<p>"
    result
  end
  private
  def total_charge
    @rentals.inject(0) { |sum, rental| sum + rental.charge }
  end
  def total_frequent_renter_points
    @rentals.inject(0) { |sum, rental| sum + rental.frequent_renter_points}
  end
end
=end
=begin
class Foo
   def foo
     "foo"
   end
   def bar
#     "foo"
     "bar"
   end
end
=end
=begin
jaws = Movie.new("Jaws", Movie::NEW_RELEASE)
et = Movie.new("ET", Movie::REGULAR)
anpanman = Movie.new("Anpanman", Movie::CHILDRENS)
first_rental = Rental.new(jaws, 3)
second_rental = Rental.new(et, 5)
third_rental = Rental.new(anpanman, 2)
snowmi = Customer.new("Snowmi")
snowmi.add_rental(first_rental)
 puts "----- Only first_rental -----"
 puts snowmi.statement
snowmi.add_rental(second_rental)
 puts "----- Add second_rental -----"
 puts snowmi.statement
snowmi.add_rental(third_rental)
 puts "----- Add third_rental -----"
 puts snowmi.statement
# puts "html section"
# puts snowmi.html_statement
=end
require 'test/unit'
require './test'
=begin
class TC_Foo < Test::Unit::TestCase
  def setup
    @obj = Foo.new
  end
  # def teardown
  # end
  def test_foo
    assert_equal("foo", @obj.foo)
  end
  def test_bar
    assert_equal("bar", @obj.bar)
  end
end
=end
class Test1 < Test::Unit::TestCase
  def setup
		@jaws = Movie.new("Jaws", Movie::NEW_RELEASE)
		@et = Movie.new("ET", Movie::REGULAR)
		@anpanman = Movie.new("Anpanman", Movie::CHILDRENS)
		@first_rental = Rental.new(@jaws, 3)
		@second_rental = Rental.new(@et, 5)
		@third_rental = Rental.new(@anpanman, 2)
		@snowmi = Customer.new("Snowmi")
		@snowmi.add_rental(@first_rental)
		@snowmi.add_rental(@second_rental)
		@snowmi.add_rental(@third_rental)
		@str ="Rental Record for Snowmi
 	Jaws	9
	ET	6.5
	Anpanman	1.5
Amount owed is 17.0
You earned 4 frequent renter points"
  end
  def test_statement
		assert_equal(@str, @snowmi.statement)
  end
end
# minitest_test1.rb
require 'minitest/autorun'
require 'minitest/unit'
require './test'
class TestFoo < MiniTest::Unit::TestCase
  def setup
        @jaws = Movie.new("Jaws", Movie::NEW_RELEASE)
        @et = Movie.new("ET", Movie::REGULAR)
        @anpanman = Movie.new("Anpanman", Movie::CHILDRENS)
        @first_rental = Rental.new(@jaws, 3)
        @second_rental = Rental.new(@et, 5)
        @third_rental = Rental.new(@anpanman, 2)
        @snowmi = Customer.new("Snowmi")
        @snowmi.add_rental(@first_rental)
        @snowmi.add_rental(@second_rental)
        @snowmi.add_rental(@third_rental)
        @str = "Rental Record for Snowmi
# {"\t"}Jaws#{"\t"}9
# {"\t"}ET#{"\t"}6.5
# {"\t"}Anpanman#{"\t"}1.5
Amount owed is 17.0
You earned 4 frequent renter points"
  end
  def test_statement
        assert_equal(@str, @snowmi.statement)
  end
end