Ruby
test
Refactoring

リファクタリング:Rubyエディションのサンプルのリファクタリング前とリファクタリング後の写経とtest/unitのテストコード

More than 1 year has passed since last update.

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
#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
test_test1.rb
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
#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