如何在Ruby单元测试中调试“未定义的方法”?

时间:2013-05-06 12:27:17

标签: ruby ruby-2.0

我正在阅读一本关于Ruby的书,我收到一个奇怪的错误,如果有人能为我解决这个问题会很高兴。

我正在寻找Ruby中的集合(非常基础)

file: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

如果你能告诉我为什么会这样,我会很高兴。

1 个答案:

答案 0 :(得分:6)

您已在append上定义SongList,但尚未在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并触发异常。