Search code examples
rubyooprspectddbdd

Can't test class within a module in RSpec


I am learning RSpec and seem to be failing miserably with any code outside of a tutorial. Which is why I am banging my head against the wall with this problem. Hopefully you all can help.

lib/NegotiGate

module NegotiGate
  class Negotiation

    def initialize(price1, price2)
      @price1 = price1
      @price2 = price2
    end

    def both_prices_equal?(price1, price2)
      if @price1 == @price2
        true
      else
        false
      end
    end

  end
end

spec/NegotiGate_spec.rb

describe NegotiGate do

  before(:each) do
    @negotiate = Negotiation.new(10,10)
  end

  describe "Negotiation" do
    describe ".both_prices_equal?" do
      context "given the sellers price is the same as the buyers highest buying price" do
        it 'returns true' do
          expect(@negotiate.both_prices_equal?).to be_true
        end
      end
    end
  end
end

Output:

NegotiGate
  Negotiation
    .both_prices_equal?
      given the sellers price is the same as the buyers highest buying price
        returns true (FAILED - 1)

Failures:

  1) NegotiGate Negotiation .both_prices_equal? given the sellers price is the same as the buyers highest buying price returns true
     Failure/Error: @negotiate = Negotiation.new(10,10)

     NameError:
       uninitialized constant Negotiation
     # ./spec/NegotiGate_spec.rb:6:in `block (2 levels) in <top (required)>'

Finished in 0.00134 seconds (files took 0.15852 seconds to load)
1 example, 1 failure

Failed examples:

rspec ./spec/NegotiGate_spec.rb:11 # NegotiGate Negotiation .both_prices_equal? given the sellers price is the same as the buyers highest buying price returns true

Any help is greatly appreciated from a student of TDD. Cheers!


Solution

  • describe blocks in RSpec don't affect Ruby namespacing. In your spec, you need to refer to Negotiation as NegotiGate::Negotiation everywhere.

    However, what you're really describing in that spec is not NegotiGate but NegotiGate::Negotiation, so change the describe block to describe NegotiGate::Negotiation and then you can use described_class for short:

    describe NegotiGate::Negotiation do
      before(:each) do
        @negotiate = described_class.new(10,10)
      end
    
      describe ".both_prices_equal?" do
        context "given the sellers price is the same as the buyers highest buying price" do
          it 'returns true' do
            expect(@negotiate.both_prices_equal?).to be_true
          end
        end
      end
    end
    

    By the way, check out RSpec's let, which is the modern way to define variables used in multiple tests. Actually in what you show you should just declare a local in your example, but you'll presumably write more tests and it will then be worth defining negotiate once. Oh, and naming it negotiation to match the class name would be nicer than negotiate.