2013-05-06 81 views
0

我正在关注Ruby的一本书,我收到了一个奇怪的错误,并且如果有人能够为我整理它,我会很高兴。如何在Ruby单元测试中调试“未定义的方法”?

我寻找到的集合在Ruby中(很基本的)

文件:song_list.rb

class Song 
    attr_accessor :name 
    attr_accessor :artist 
    attr_accessor :duration 

    def initialize(name, artist, duration) 
     @name = name 
     @artist = artist 
     @duration = duration 
    end 

    def to_s 
     "SongIs - #{@name} - #{@artist} - #{@duration}" 
    end 
end 

class SongList 
    def initialize 
     @songs = Array.new 
    end 

    def append(song) 
     @songs.push(song) 
    end 

    def delete_first 
     @songs.shift 
    end 

    def delete_last 
     @songs.pop 
    end 

    def [](index) 
     @songs[index] 
    end 
end 

然后我写了一个测试:test_unit.rb现在,当

require_relative 'song_list' 
require 'test/unit' 
class TestSongList < Test::Unit::TestCase 
    def test_delete 
     list = SongList.new 
     s1 = Song.new('title1', 'artist1', 1) 
     s2 = Song.new('title2', 'artist2', 2) 
     s3 = Song.new('title3', 'artist3', 3) 
     s4 = Song.new('title4', 'artist4', 3) 

     list.append(s1).append(s2).append(s3).append(s4) 

     assert_equal(s1, list[0]) 
     assert_equal(s2, list[1]); 
     assert_equal(s3, list[2]); 
     assert_equal(s4, list[3]); 

     assert_nil(list[9]) 

     assert_equal(s1, list.delete_first) 
     assert_equal(s2, list.delete_first) 
     assert_equal(s4, list.delete_last) 
     assert_equal(s3, list.delete_last) 
     assert_nil(list.delete_last) 

    end 
end 

我运行第二个文件ruby test_unit.rb,我得到一个错误。错误说我没有在RubyList类中定义函数append

但我有,不是吗?这是错误。

Run options: 

# Running tests: 

[1/1] TestSongList#test_delete = 0.00 s 
    1) Error: 
test_delete(TestSongList): 
NoMethodError: undefined method `append' for #<Array:0x9f52b30> 
    test_unit.rb:11:in `test_delete' 
    /home/ziyan/.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/minitest/unit.rb:1301:in `run' 
    /home/ziyan/.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/test/unit/testcase.rb:17:in `run' 
    /home/ziyan/.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/minitest/unit.rb:919:in `block in _run_suite' 
    /home/ziyan/.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/minitest/unit.rb:912:in `map' 
    /home/ziyan/.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/minitest/unit.rb:912:in `_run_suite' 
    /home/ziyan/.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/test/unit.rb:657:in `block in _run_suites' 
    /home/ziyan/.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/test/unit.rb:655:in `each' 
    /home/ziyan/.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/test/unit.rb:655:in `_run_suites' 
    /home/ziyan/.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/minitest/unit.rb:867:in `_run_anything' 
    /home/ziyan/.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/minitest/unit.rb:1060:in `run_tests' 
    /home/ziyan/.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/minitest/unit.rb:1047:in `block in _run' 
    /home/ziyan/.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/minitest/unit.rb:1046:in `each' 
    /home/ziyan/.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/minitest/unit.rb:1046:in `_run' 
    /home/ziyan/.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/minitest/unit.rb:1035:in `run' 
    /home/ziyan/.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/test/unit.rb:21:in `run' 
    /home/ziyan/.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/test/unit.rb:774:in `run' 
    /home/ziyan/.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/test/unit.rb:366:in `block (2 levels) in autorun' 
    /home/ziyan/.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/test/unit.rb:27:in `run_once' 
    /home/ziyan/.rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/test/unit.rb:365:in `block in autorun' 

Finished tests in 0.004259s, 234.8127 tests/s, 0.0000 assertions/s. 
1 tests, 0 assertions, 0 failures, 1 errors, 0 skips 

如果你能向我描述为什么发生这种情况,我会很高兴。

回答

6

您已在SongList上定义append,但您尚未在Array上定义它。您的通话append这里的问题是:

list.append(s1).append(s2).append(s3).append(s4) 

如果你想链方法调用这样,你就需要调整SongList#append方法是:

def append(song) 
    @songs.push(song) 
    self 
end 

,同样为您的其他方法。另外,更改初始电话是:

list.append(s1) 
list.append(s2) 
list.append(s3) 
list.append(s4) 

,或者使用一个循环:

[s1, s2, s3, s4].each{|s| list.append(s)} 

的原因错误是因为没有明确的return调用Ruby方法将返回的最后一行的值的方法。在你的情况下,这是@songs.push(song)@songs是一个数组,Array#push返回一个数组,因此你的append方法返回一个数组。链中的第二个调用尝试从第一个调用返回的数组上调用append并触发异常。

+0

哦,我明白了,所以红宝石是返回函数的最后一行......让我测试一下,并接受。感谢您提供丰富的答案! – 2013-05-06 14:22:33

+0

准确地说 - 它返回最后一行*评估为*的任何内容。所以如果你有一个方法:'def addone(n)n + 1; end','addone(1)'会返回'2'。在这种情况下,这种简单的方法只有1行,所以它可以像你期望的那样工作,但总的原则是一样的。 – 2013-05-06 14:32:50

+0

我正在爱上Ruby的那一刻:)感谢它的工作就像一个魅力! – 2013-05-06 16:50:41