使用rspec时用irb加载程序

时间:2015-01-19 19:07:03

标签: ruby rspec irb

我正在用ruby创建一个简单的二十一点程序。我这样做是为了研究我的TDD技能,因此我使用Rspec进行测试覆盖。为了保持工作更清洁,我的文件结构是一个名为blackjack的主文件夹。其中包括lib和spec文件夹。目前在lib中有一个deck.rb和card.rb文件。然后,我为每个都有追溯规格文件夹。当我尝试在irb中加载程序时,我遇到了这样的问题。如果我想测试两个文件夹之间分割的方法,我有点困惑,我应该怎么做呢。当单独加载卡组和卡文件时,我会收到以下错误。

2.1.1 :001 > require "./deck.rb"
 => true 
2.1.1 :002 > cards = Deck.build_cards
NameError: uninitialized constant Deck::Card
    from /Users/em/ruby/blackjack/lib/deck.rb:7:in `block (2 levels) in build_cards'
    from /Users/em/ruby/blackjack/lib/deck.rb:6:in `each'
    from /Users/em/ruby/blackjack/lib/deck.rb:6:in `block in build_cards'
    from /Users/em/ruby/blackjack/lib/deck.rb:5:in `each'
    from /Users/em/ruby/blackjack/lib/deck.rb:5:in `build_cards'
    from (irb):2
    from /Users/em/.rvm/rubies/ruby-2.1.1/bin/irb:11:in `<main>'

2.1.1 :001 > require './card'
 => true 
2.1.1 :002 > cards = Deck.build_cards
NameError: uninitialized constant Deck
    from (irb):2
    from /Users/em/.rvm/rubies/ruby-2.1.1/bin/irb:11:in `<main>'

如果有人可以帮我澄清一些事情,我将不胜感激。我已经包含了下面的其余代码。

card_spec.rb

require 'card'
require 'deck'

describe Card do 

  it "should accept a suit and value when building" do 
    card = Card.new(:clubs, 10)
    expect(card.suit).to eq (:clubs)
    expect(card.value).to eq(10)
  end 

  it "should have a value of 10 for facecards" do 
    facecards = ["J", "Q", "K"]
    facecards.each do |facecard|
        card = Card.new(:hearts, facecard)
        expect(card.value).to eq(10)
    end
  end
  it "should have a value of 4 for the 4-clubs" do 
    card = Card.new(:clubs, 4)
    expect(card.value).to eq(4)
  end

  it "should return 11 for Ace" do 
    card = Card.new(:diamonds, "A")
    expect(card.value).to eq(11)
  end

  it "should be formatted nicely" do 
    card = Card.new(:diamonds, "A")
    expect(card.to_s).to eq("A-diamonds")
  end
end

deck_spec.rb

require 'card'
require 'deck'

describe Deck do 

    it 'should build 52 cards' do 
        expect(Deck.build_cards.length).to eq(52)
    end
end

card.rb

require 'deck'
class Card

    attr_reader :suit, :value
    def initialize(suit, value) 
        @suit = suit
        @value = value
        #the value here is what the card should return-facevalue
    end

    def value
        return 10 if ["J", "Q", "K"].include?(@value) 
        return 11 if @value == "A"
        return @value
    end

    def to_s
        "A-diamonds"
        "#{@value}-#{suit}"
    end
end

deck.rb

class Deck
    def self.build_cards
        cards = []
        [:clubs, :diamonds, :spades, :hearts].each do |suit|
            (2..10).each do |number|
            cards << Card.new(suit, number)
        end
        ["J", "Q", "K", "A"].each do |facecard|
            cards << Card.new(suit, facecard)
        end
    end
    cards
end
    #going to occur on the class as it is a class method and will execute once
    #The idea of a deck needs to be able to build a deck
end

1 个答案:

答案 0 :(得分:3)

您需要将require "card"添加到deck.rb,因为您在Card中引用了.build_cards。您还需要将lib添加到加载路径中,因为在Ruby中不再为您完成。您可以在命令行上执行此操作:

irb -Ilib
ruby -Ilib

请注意,rspec会自动为您执行此操作,这就是您的规范有效的原因。

如果需要(例如,如果您在bin中有一个脚本要加载lib中的文件),您也可以在代码中执行此操作。

# Assuming this code is in bin
$LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)

加载路径说明

-I标志将目录添加到$LOAD_PATH全局变量。您可以直接在程序中检查:

> ruby -e 'puts $LOAD_PATH'
/Users/xavier/.rubies/ruby-2.1.3/lib/ruby/site_ruby/2.1.0
/Users/xavier/.rubies/ruby-2.1.3/lib/ruby/site_ruby/2.1.0/x86_64-darwin13.0
/Users/xavier/.rubies/ruby-2.1.3/lib/ruby/site_ruby
/Users/xavier/.rubies/ruby-2.1.3/lib/ruby/vendor_ruby/2.1.0
/Users/xavier/.rubies/ruby-2.1.3/lib/ruby/vendor_ruby/2.1.0/x86_64-darwin13.0
/Users/xavier/.rubies/ruby-2.1.3/lib/ruby/vendor_ruby
/Users/xavier/.rubies/ruby-2.1.3/lib/ruby/2.1.0
/Users/xavier/.rubies/ruby-2.1.3/lib/ruby/2.1.0/x86_64-darwin13.0
> ruby -Itesting -e 'puts $LOAD_PATH'
/Users/xavier/Code/ex/ruby/testing
/Users/xavier/.rubies/ruby-2.1.3/lib/ruby/site_ruby/2.1.0
/Users/xavier/.rubies/ruby-2.1.3/lib/ruby/site_ruby/2.1.0/x86_64-darwin13.0
/Users/xavier/.rubies/ruby-2.1.3/lib/ruby/site_ruby
/Users/xavier/.rubies/ruby-2.1.3/lib/ruby/vendor_ruby/2.1.0
/Users/xavier/.rubies/ruby-2.1.3/lib/ruby/vendor_ruby/2.1.0/x86_64-darwin13.0
/Users/xavier/.rubies/ruby-2.1.3/lib/ruby/vendor_ruby
/Users/xavier/.rubies/ruby-2.1.3/lib/ruby/2.1.0
/Users/xavier/.rubies/ruby-2.1.3/lib/ruby/2.1.0/x86_64-darwin13.0

注意第二次调用如何包含额外的testing目录。如果您使用IRB进行了类似的测试,您会发现如果-I lib目录不在$LOAD_PATH,则无法找到您需要的文件。

Ruby使用此$LOAD_PATH变量来确定在需要时查看的位置。在伪代码中:

def require(filename)
  $LOAD_PATH.each do |dir|
    path = File.join(dir, filename)
    if File.exist?(path)
      load(path)
      return
    end
  end
end

它实际上比那更复杂(和更有效),但这是它的要点。如果你对我talked about it some at RubyConf 2013感兴趣。

其他相关要点并未直接回答您的问题:

您还应该从require "deck"删除card.rb,因为该文件中的任何内容都不需要了解Deck。如果你有警告,这会警告你“循环要求”,如果你有两个文件相互加载会发生这种情况。这通常是一种不好的做法。 (要明确的是:这不是你在这里做的,因为card.rb

您的规格应该只需要正在测试的特定内容。在这种情况下,deck.rb应该需要卡片,card_spec.rb应该需要卡片。他们不需要两者都需要。

IRB要求的deck_spec.rb是多余的,不是必需的。