Ruby on rails RSpec重构帮助
当涉及到测试或测试驱动的开发时,我是一个大傻瓜,所以我真的很难测试我的代码。最大的原因是这样的情况 我有一个模型,Ruby on rails RSpec重构帮助,ruby-on-rails,ruby,testing,refactoring,rspec,Ruby On Rails,Ruby,Testing,Refactoring,Rspec,当涉及到测试或测试驱动的开发时,我是一个大傻瓜,所以我真的很难测试我的代码。最大的原因是这样的情况 我有一个模型,Photo,它有“地点”、“城市”、“州”和“国家”字段 我创建了一个方法“location”,它可以接受一个参数,并将返回最小形式、简短形式或完整形式的location。以下是模型代码: def location( form=:full ) usa = country.eql?('US') || country.eql?('United States') country_n
Photo
,它有“地点”、“城市”、“州”和“国家”字段
我创建了一个方法“location”,它可以接受一个参数,并将返回最小形式、简短形式或完整形式的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中测试这一点所需的时间要比在控制台中测试所需的时间多得多
因此,我有两个问题:
说清楚一点,我最感兴趣的是重构测试代码,而不是定位方法。一个很好的观察结果。我的陪审团对TDD的合理性也持同样的观点。只是在这里检查一下,但是您是否正在运行类似于自动测试+咆哮者(Mac OS)的程序来加快测试速度?您可能想查看gem
spork
,或许可以仔细研究这个线程,以获得许多关于加快rspec执行的好建议
所有这些都不能真正解决您对(rspec代码)/(程序代码)比率如此之高的评论。我也没有忘记调试测试代码的讽刺意味。作为ruby和rails的新手,我对测试驱动的开发持开放态度,但我仍然怀疑这更像是一种隐藏的成本,而不是开发模式的良好特性。我发现高测试/代码比率是完全正常的,甚至是需要的。一般来说,测试代码应该比普通代码更容易“分解”。它们被称为示例是有原因的:测试应该向人们展示如何使用您的代码,最好的方法是对每个示例都非常简单 编写相对冗长的测试也有助于避免调试它们。测试应该简单易读,而不是紧凑高效。当您不必首先找出任何复杂的循环、迭代、递归或元编程时,就更容易理解每个测试在做什么
也就是说,在寻找可以放在before(:each)块中的代码段时,应该始终保持警惕。看起来你们在这方面都做得很好。我重构了你们的
位置方法,应该注意的是,我能够这样做并确保我没有破坏任何东西的唯一原因是因为。。。等等。。。你有测试
这不一定更好,也不一定性能更好,但在我看来,它更具可读性。我相信这一点还可以进一步改进
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
如果您真的关心规格的行数,那么有一些方法可以减少规格的行数。自定义匹配器是一个很好的匹配器
关键是,这些测试可以防止其他人,甚至你自己,破坏你编写的代码。当然,您可以在控制台中进行测试,但这可能会变得非常复杂、乏味,而且更容易出现人为错误。我正在运行自动测试,但我没有咆哮者,因此我应该注意这一点!也谢谢你的链接!罗伯特,谢谢你的例子。我同意你的代码更清晰,尽管我有点喜欢数组驱动的case块:)但是你对重构规范有什么建议吗?或者这些规格对你来说正常吗。我明白你关于测试在共享代码中的价值的观点,我真正的挑战是对于这样一个公式,我花了大约10倍的时间来编写测试,而不是编写让它们通过的代码。我正在寻找编写更好的测试的方法,这样我就不会花太长时间来编写它们,以至于我根本不想编写它们……这只是“我在这里试图学习的东西”的另一点,我觉得思考我需要的代码比思考验证它的测试要容易得多。因此,关于如何“思考测试”的建议也会很有用。对于您正在测试的内容,规范看起来和我预期的一样。充分利用上下文和前块来限制重复。为了进一步减少重复,我将更多地研究重构location方法。将其设置为:full-chains到:short,这样当您测试:full时,您只需测试与:short不同的内容。