Справка по рефакторингу RSpec - PullRequest
4 голосов
/ 23 марта 2011

Я большой нуб, когда дело доходит до тестирования или разработки, основанной на тестировании, поэтому я действительно борюсь с тестированием своего кода. Самая большая причина, почему такие ситуации ...

У меня есть модель, Photo, в которой есть поля "место", "город", "штат" и "страна".

Я создал метод "location", который может принимать один аргумент и будет возвращать либо минимальную форму, краткую форму, либо полную форму местоположения. Вот код модели:

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

Как видите, это довольно чисто и лаконично. Теперь вот моя спецификация:

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

Итак, 98 строк тестового кода для тестирования 17 строк модельного кода. Это просто кажется мне безумным. Кроме того, время, необходимое для тестирования этого в RSpec, откровенно говоря, намного больше, чем время, необходимое для тестирования этого в консоли.

Итак, у меня два вопроса:

  1. Конечно, есть лучший способ сделать это. Кто-нибудь может предложить рефакторинг?
  2. Является ли это соотношение тестового кода к коду модели нормальным, и если да, то почему оно стоит времени?

Спасибо !!

- РЕДАКТИРОВАТЬ -

Просто чтобы прояснить ситуацию, меня больше всего интересует рефакторинг кода теста , а не метод определения местоположения.

Ответы [ 3 ]

3 голосов
/ 24 марта 2011

Я обнаружил, что высокое соотношение тест / код совершенно нормально и даже желательно.Обычно тестовый код должен быть более «распутанным», чем обычный код.Их называют примерами по причине: тесты должны показать людям, как использовать ваш код, и лучший способ сделать это - быть глупо простым с каждым примером.

Написание относительно длинных тестов также помогает избежать необходимости их отладки.Тесты должны быть простыми и удобочитаемыми, а не компактными и эффективными.Гораздо проще понять, что делает каждый тест, когда вам не нужно сначала выяснять какие-либо сложные циклы, итерации, рекурсию или метапрограммирование.которые могут быть помещены в блок before (: each).Похоже, у вас все хорошо на этом фронте.

2 голосов
/ 23 марта 2011

Отличное наблюдение. Мое жюри так же рассуждает о здравомыслии TDD. Просто проверяю здесь, но запускаете ли вы что-то вроде автотест + Growler (Mac OS) для ускорения тестирования? Возможно, вы захотите заглянуть в гем spork и, возможно, заглянуть в этот поток Почему RSpec так медленно работает в Rails? , чтобы получить много полезных советов по ускорению выполнения rspec.

Ничто из этого не учитывает ваши комментарии о том, что соотношение (код rspec) / (программный код) так высоко. Ирония необходимости отладки моего тестового кода не теряется и на меня. Будучи счастливым и увлеченным новичком в ruby ​​и rails, я непредвзято отношусь к тестированию управляемой разработки, но я все еще подозреваю, что это скорее скрытая стоимость , чем хорошая функция парадигма развития.

0 голосов
/ 25 марта 2011

Я реорганизовал ваш location метод, и следует отметить, что единственная причина, по которой я мог это сделать и быть уверенным, что я ничего не сломал, заключалась в том, что ... подождите ... у вас были тесты!

Это не обязательно лучше или более производительно, но, на мой взгляд, это гораздо более читабельно. И я уверен, что это может быть улучшено.

  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

Существуют способы уменьшить количество строк в ваших спецификациях, если вы действительно заботитесь об этом числе. Настраиваемые совпадения - отличный выбор.

Суть в том, что тесты не позволяют другим людям, и даже вам позже, нарушать код, который вы написали. Конечно, вы можете просто протестировать что-то в консоли, но это может очень быстро усложниться, утомительно и более подвержено человеческим ошибкам.

...