How to test class methods in RSPEC

39,481

Solution 1

This is an interesting though perhaps more obtuse way to use the 'subject' block with Class methods.

Edit: The broken link as reported by the Wayback Archive which I suppose is susceptible to the same problem.

Solution 2

Apparently there is a described_class method.

https://www.relishapp.com/rspec/rspec-core/docs/metadata/described-class

I suppose it's cleaner than subject.class, since it doesn't introduce another . method call, which reduces readability.

Using either described_class or subject.class may be more DRY than mentioning the class explicitly in every example. But personally I think not getting the syntax highlighting that comes with mentioning the class name explicitly is kind of a bummer, and I think it reduces readability, despite it totally winning in the maintainability department.

A question arises regarding best practice:

Should you use described_class whenever possible inside and outside the .expect() method, or only within the expect() method?

Solution 3

There isn't a subject equivalent for calling a method, so using it is the way to go here. The issue I see with your code as presented is that it doesn't actually explain what you are testing for. I would write something more like:

describe Buy do
  describe '.get_days' do
    it 'should detect hyphenated weeknights' do
      Buy.get_days('Includes a 1-weeknight stay for up to 4 people').should == 1
    end
    it 'should detect hyphenated nights' do
      Buy.get_days('Includes a 1-night stay in a King Studio Room with stone fireplace').should == 1
    end
    it 'should detect first number' do
      Buy.get_days('Includes 4 nights/5 days at the Finisterra Hotel for up to two adults and two children (staying in the same room)').should == 4
    end
  end
end

I'm making assumptions about what you're after here, but hopefully the idea is clear. This will also lead to much more helpful error output when a test fails. Hope this helps!

Solution 4

This might be an old question but you can always use subject.class to get by:

describe Buy do
  describe '.get_days' do
    it { expect(subject.class.get_days('Includes a 1-weeknight stay for up to 4 people')).to eq 1 }
  end
end
Share:
39,481

Related videos on Youtube

lulalala
Author by

lulalala

Ruby and Rails developer. https://github.com/lulalala/ I also worked on XSLT, and have a hobby project written in C#.

Updated on March 19, 2020

Comments

  • lulalala
    lulalala about 4 years

    I wrote a simple class method Buy.get_days(string), and is trying to test it with different text string inputs. However I feel it is very verbose.

    • Is there any more concise way to test the following?
    • Is there a equivalent of subject for methods which I can just keep passing different parameters in and check the results?
    • Is there a way to avoid the unnecessary description at each it?

    thanks

     describe Buy do
       describe '.get_days' do
        it 'should get days' do
          Buy.get_days('Includes a 1-weeknight stay for up to 4 people')
          .should == 1
          end
        it 'should get days' do
          Buy.get_days('Includes a 1-night stay in a King Studio Room with stone fireplace')
          .should == 1
        end
        it 'should get days' do
          Buy.get_days('Includes 4 nights/5 days at the Finisterra Hotel for up to two adults and two children (staying in the same room)')
          .should == 4
        end
      end
    end
    
    • Dave Newton
      Dave Newton over 12 years
      How is the it description unnecessary? Just because you wrote the same text for specs that test different things doesn't mean the description shouldn't be there--maybe re-word them so they're useful?
    • lulalala
      lulalala over 12 years
      the input/output combination is descriptive enough (for me at least).
    • ahnbizcad
      ahnbizcad about 9 years
      can you give an example of rewording to make it more useful, @DaveNewton ?
    • Dave Newton
      Dave Newton about 9 years
      @ahnbizcad Hopefully three different blocks, each of which 'should get days' (e.g., they say the same thing), is an obvious smell. Why are the expectations different? There's something about them that makes the result be 1, 1, and 4. The purpose of the string arg to it is to describe in human-readable terms what the block is actually testing. I can't reword the OP's descriptions because I don't know why the blocks should return the blocks that they do.
  • lulalala
    lulalala over 12 years
    I guess the answer at the end is: there is no more concise way of writing it.
  • Jared Beck
    Jared Beck almost 11 years
    broken link says "Posterous Spaces is no longer available"
  • ahnbizcad
    ahnbizcad about 9 years
    Good catch! facepalm. I should have known this. This is the correct answer!
  • B Seven
    B Seven about 9 years
    subject.class? Wouldn't it be clearer to use Buy?
  • Dave Newton
    Dave Newton about 9 years
    @ahnbizcad Not really, because this shows only a single test. Think about the spec output and what it's supposed to be showing. Adding the other tests with no descriptive tests makes for essentially useless spec output because you don't know (a) what the spec is testing, and (b) what differentiates the different specs.
  • Dave Newton
    Dave Newton about 9 years
    @lulalala You don't want it to be concise, you want it to be accurate and descriptive in the context of the specs.
  • IAmNaN
    IAmNaN over 7 years
    Fast-forward three years and the gist is here: gist.github.com/knzconnor/816049
  • DBrown
    DBrown over 6 years
    This is what I prefer to use in my tests, leaving subject for the data tested through said methods.
  • Dave Newton
    Dave Newton over 4 years
    This is one reason why link-only answers are bad.