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