下面是两个相同的类,它们与运算符+
和<<
不同。这些可以在注入方法中找到。在+
情况下,测试通过,<<
中的一些测试失败。为什么呢?
class Integer
ROMAN_NUMERALS = {
0 => '',
1 => 'I', 2 => 'II', 3 => 'III', 4 => 'IV', 5 => 'V', 6 => 'VI', 7 => 'VII', 8 => 'VIII', 9 => 'IX',
10 => 'X', 20 => 'XX', 30 => 'XXX', 40 => 'XL', 50 => 'L', 60 => 'LX', 70 => 'LXX', 80 => 'LXXX', 90 => 'XC',
100 => 'C', 200 => 'CC', 300 => 'CCC', 400 => 'CD', 500 => 'D', 600 => 'DC', 700 => 'DCC', 800 => 'DCCC', 900 => 'CM',
1000 => 'M', 2000 => 'MM', 3000 => 'MMM'
}
def to_roman
to_s.reverse.chars.each_with_index.inject("") do |roman_numeral, (character, index)|
ROMAN_NUMERALS[character.to_i * 10 ** index] << roman_numeral
end
end
end
我在运行
时会得到不同的结果class Integer
ROMAN_NUMERALS = {
0 => '',
1 => 'I', 2 => 'II', 3 => 'III', 4 => 'IV', 5 => 'V', 6 => 'VI', 7 => 'VII', 8 => 'VIII', 9 => 'IX',
10 => 'X', 20 => 'XX', 30 => 'XXX', 40 => 'XL', 50 => 'L', 60 => 'LX', 70 => 'LXX', 80 => 'LXXX', 90 => 'XC',
100 => 'C', 200 => 'CC', 300 => 'CCC', 400 => 'CD', 500 => 'D', 600 => 'DC', 700 => 'DCC', 800 => 'DCCC', 900 => 'CM',
1000 => 'M', 2000 => 'MM', 3000 => 'MMM'
}
def to_roman
to_s.reverse.chars.each_with_index.inject("") do |roman_numeral, (character, index)|
ROMAN_NUMERALS[character.to_i * 10 ** index] + roman_numeral
end
end
end
我正在使用的测试位于
之下require 'minitest/autorun'
require_relative 'roman'
class RomanTest < MiniTest::Unit::TestCase
def test_1
assert_equal 'I', 1.to_roman
end
def test_2
assert_equal 'II', 2.to_roman
end
def test_3
assert_equal 'III', 3.to_roman
end
def test_4
assert_equal 'IV', 4.to_roman
end
def test_5
assert_equal 'V', 5.to_roman
end
def test_6
assert_equal 'VI', 6.to_roman
end
def test_9
assert_equal 'IX', 9.to_roman
end
def test_27
assert_equal 'XXVII', 27.to_roman
end
def test_48
assert_equal 'XLVIII', 48.to_roman
end
def test_59
assert_equal 'LIX', 59.to_roman
end
def test_93
assert_equal 'XCIII', 93.to_roman
end
def test_141
assert_equal 'CXLI', 141.to_roman
end
def test_163
assert_equal 'CLXIII', 163.to_roman
end
def test_402
assert_equal 'CDII', 402.to_roman
end
def test_575
assert_equal 'DLXXV', 575.to_roman
end
def test_911
assert_equal 'CMXI', 911.to_roman
end
def test_1024
assert_equal 'MXXIV', 1024.to_roman
end
def test_3000
assert_equal 'MMM', 3000.to_roman
end
end
在一种情况下查看规格如何失败,而在另一种情况下则不然。我认为这些都是以同样的方式工作。
答案 0 :(得分:3)
这一行是一个问题...
ROMAN_NUMERALS[character.to_i * 10 ** index] << roman_numeral
它将返回一个字符串,该字符串是正确的ROMAN_NUMERALS
键加roman_numeral
的值,这是您想要的,但它也会更改ROMAN_NUMERALS
哈希中的值! shovel
运算符<<
更改运算符左侧的字符串(我们称之为变异运算符)。
因此,如果你测试1001,单位1
将返回“I”(那很好),那么零将返回一个空字符串BUT会将零值更改为“I”...第二个零将返回“I”(不正确)并将零值更改为“II”。千位中的1
将返回“M”,但随后将哈希值更改为“MII”。
答案 1 :(得分:2)
当执行第ROMAN_NUMERALS[character.to_i * 10 ** index] << roman_numeral
行时,您将使用其值加character.to_i * 10 ** index
替换与键roman_numeral
对应的值。
答案 2 :(得分:2)
现在您的问题已经得到解答,我想建议一种替代方法,也可以采用不同的方式来执行测试。这需要Ruby v1.9 +,因此我们可以依赖于散列键的顺序。
<强>代码强>
首先,颠倒哈希元素的顺序。
RNR = Hash[ROMAN_NUMERALS.to_a.reverse]
#=> {3000=>"MMM", 2000=>"MM", 1000=>"M",..., 2=>"II", 1=>"I", 0=>""}
然后:
class Integer
def to_roman
num = self
roman = ""
while num > 0
i,r = RNR.find { |i,r| i <= num }
roman << r
num -= i
end
roman
end
end
测试目标
我们需要测试大量的整数值,并确保我们正在测试每个整数与正确的罗马数字等价物。通过创建将罗马数字转换为整数的方法,可以满足这两个目标:
RNRI = RNR.invert
#=> {"MMM"=>3000, "MM"=>2000, "M"=>1000,..., "II"=>2, "I"=>1, ""=>0}
class String
def roman_to_integer
num = 0
roman = self
while roman.size > 0
r, i = RNRI.find { |r,m| roman =~ /^#{r}/ }
num += i
roman = roman[r.size..-1]
end
num
end
end
<强>实施例强>
现在让我们调用Integer#to_roman
和String#roman_to_integer
来获取各种整数值:
def check_one(i)
roman = i.to_roman
puts "#{i}.to_roman = #{roman}, #{roman}.roman_to_integer = " +
#{roman.roman_to_integer}"
end
check_one(402) # 'CDII'
# 402.to_roman = CDII, CDII.roman_to_integer = 402
check_one(575) # 'DLXXV'
# 575.to_roman = DLXXV, DLXXV.roman_to_integer = 575
check_one(911) # 'CMXI'
# 911.to_roman = CMXI, CMXI.roman_to_integer = 911
check_one(1024) # 'MXXIV'
# 1024.to_roman = MXXIV, MXXIV.roman_to_integer = 1024
check_one(3000) # 'MMM'
# 3000.to_roman = MMM, MMM.roman_to_integer = 3000
<强>测试强>
所以现在在你的测试中你可以使用:
def test_all(n)
(1..n).each { |i| test_one(i) }
end
def test_one(i)
roman = i.to_roman
assert_equal(i, roman.roman_to_integer, "#{i}.to_roman=#{roman}, " +
"#{roman}.roman_to_integer = #{roman.roman_to_integer}")
end