RSpec refactoring help

I'm a big noob when it comes to testing or test-based development, so I'm really struggling with testing my code. The biggest reason why such situations ...

I have a model, Photo , which has the fields "place", "city", "state" and "country".

I created a "location" method that can take one argument and will return either a minimal form, or a short form, or the full location of the form. Here's the model code:

 def location( form=:full ) usa = country.eql?('US') || country.eql?('United States') country_name = Carmen::country_name( self.country ) if self.state.present? state_name = Carmen::state_name( self.state ) end case [form, usa] when [:min, true] then ( self.city.blank? ? self.place : self.city ) when [:min, false] then ( self.city.blank? ? self.place : self.city ) when [:short, true] then [ ( self.city.blank? ? self.place : self.city ), state_name ].join(', ') when [:short, false] then [ ( self.city.blank? ? self.place : self.city ), country_name ].join(', ') when [:full, true] then [ self.place, self.city, state_name ].delete_if{|a| a.blank? }.join(', ') when [:full, false] then [ self.place, self.city, country_name ].delete_if{|a| a.blank? }.join(', ') else raise "Invalid location format" end end 

As you can see, this is pretty clean and eloquent. Now, here is my specification:

 describe "location" do before do @us = Photo.new(:place => "Main St.", :city => "Barville", :state => "TX", :country => "US") @uk = Photo.new(:place => "High St.", :city => "Barchester", :country => "GB") @it = Photo.new( :place => "il Commune", :city => "Baria", :state => "AR", :country => "IT" ) end context "minimum form" do it "should return city or place for US Photos" do @us.location(:min).should == "Barville" @us.city = "" @us.location(:min).should == "Main St." end it "should return city or place for Internationals without states" do @uk.location(:min).should == "Barchester" @uk.city = "" @uk.location(:min).should == "High St." end it "should return city or place for Internationals with states" do @it.location(:min).should == "Baria" @it.city = "" @it.location(:min).should == "il Commune" end end context "short form" do it "should return city,state or place,state for US photos" do @us.location(:short).should == "Barville, Texas" @us.city = "" @us.location(:short).should == "Main St., Texas" end it "should return city,country or place,country for Internationals without states" do @uk.location(:short).should == "Barchester, United Kingdom" @uk.city = "" @uk.location(:short).should == "High St., United Kingdom" end it "should return city,country or place,country for Internationals with states" do @it.location(:short).should == "Baria, Italy" @it.city = "" @it.location(:short).should == "il Commune, Italy" end end context "full form" do context "US Photos" do it "should return place, city, state" do @us.location(:full).should == "Main St., Barville, Texas" end it "should return place, state if city is blank" do @us.city = "" @us.location(:full).should == "Main St., Texas" end it "should return city, state if place is blank" do @us.place = "" @us.location(:full).should == "Barville, Texas" end end context "Internationals without states" do it "should return place, city, state" do @uk.location(:full).should == "High St., Barchester, United Kingdom" end it "should return place, state if city is blank" do @uk.city = "" @uk.location(:full).should == "High St., United Kingdom" end it "should return city, state if place is blank" do @uk.place = "" @uk.location(:full).should == "Barchester, United Kingdom" end end end context "Internationals with states" do it "should return place, city, state" do @it.location(:full).should == "il Commune, Baria, Italy" end it "should return place, state if city is blank" do @it.city = "" @it.location(:full).should == "il Commune, Italy" end it "should return city, state if place is blank" do @it.place = "" @it.location(:full).should == "Baria, Italy" end end end 

So, 98 lines of test code to test 17 lines of model code. It seems crazy to me. Plus, the time it takes to test it in RSpec is frankly much more than the time it takes to test it on the console.

So, I have two questions:

  • Of course, there is a better way to do this. Can anyone suggest refactoring?
  • Is this a test code relation for a normal model code, and if so, why is it worth the time?

Thanks!!

- EDIT -

To be clear, I'm most interested in refactoring the test code , not the location method.

+4
source share
3 answers

I reorganized your location method, and it should be noted that the only reason I could do this, and to be sure that I did not violate anything, was ... because ... waited ... you had tests!

This is not necessarily better or more effective, but in my opinion, it is much more readable. And I am sure that this can be further improved.

  def location( form=:full ) if respond_to? "location_#{form}" send("location_#{form}") else raise ArgumentError, "Invalid location format" end end protected def location_min city.blank? ? place : city end def location_short [(city.blank? ? place : city), (usa? ? state_name : country_name)].join(', ') end def location_full [place, city, (usa? ? state_name : country_name)].delete_if { |v| v.blank? }.join(', ') end def usa? country.eql?('US') || country.eql?('United States') end def state_name Carmen::state_name(self.state) if self.state.present? end def country_name Carmen::country_name(self.country) end 

There are ways to reduce the number of lines in your specifications if you really care about that number. Custom matches are great.

The fact is that tests do not allow other people, and even themselves later, to abandon the code that you wrote. Of course, you can just test things in the console, but it can get complicated very quickly and tediously and is more prone to human error.

0
source

I found that a high test / code ratio is absolutely normal and even desirable. Typically, test code should be more "loose" than regular code. They are called examples for one reason: tests should show people how to use your code, and the best way to do this is to be stupidly simple with every example.

Writing relatively long tests also helps you not debug them. Tests should be clear and understandable, not compact and effective. It is much easier to understand what each test does when you do not need to first define any complex loops, iterations, recursion, or metaprogramming.

However, you should always be vigilant in looking for code segments that can be placed in a before (: each) block. It looks like you're all well on this front.

+3
source

Wonderful observation. My jury also sounds like TDD sanity. Just check here, but are you using something like autotest + growler (Mac OS) to speed up testing? You can look into the gem spork and perhaps think through this thread. Why is RSpec so slow under Rails? for a lot of good tips on speeding up rspec.

None of this really takes into account your comments regarding the ratio of (rspec code) / (code code), which are so high. The irony of having to debug my test code is also not lost. Being a happy and enthusiastic newcomer to ruby ​​and rails, I keep up to date with development based on testing, but I still doubt that this is more of a hidden cost than a good feature of the development paradigm.

+2
source

Source: https://habr.com/ru/post/1344951/


All Articles