the*_*oid 18 language-agnostic debugging tdd unit-testing saff-squeeze
我已阅读Kent Beck的原始博客帖子上的孔雀皇挤压方法.我还阅读了这篇InfoQ帖子,详细阐述了该主题,但未提供任何示例.
我知道它本质上是一种在不依赖调试器的情况下查找错误的方法.但是我发现Kent的例子并不那么明确.
更开明的人可以通过一个明确,具体的例子教育我如何使用这种方法吗?它有望成为研究该方法的其他人的学习资源.
Dav*_*uth 26
Saff Squeeze是一种系统技术,用于从失败的测试中删除测试代码和非测试代码,直到测试和代码足够小以便理解.
我同意肯特对Saff Squeeze的原始描述有点困难,部分原因是因为他正在测试的软件JUnit是高度抽象的,部分是因为他没有提供足够的第2步示例,"先发布一个(失败的)断言在测试中比现有的断言."
在他的第一轮中,他只是将测试中的断言提高了,而他对后续步骤的总结可能会让你认为在第2步中你唯一可以做的就是移动现有的断言,但是在他的最后一步,他想出了一个新的,更简单的失败断言.第2步中的断言可能只是现有的一个在测试中移动得更高,这是常见的,但它也可能是一个新的,当你对代码的理解和bug的演变时.
这是一个例子.需要 Saff Squeeze 太简单了,但它说明了这种技术.
我刚刚写了这个任务关键类:
class Autopilot
def self.fly_to(city)
runways_in_city = runways_in city
runway = closest_available runways_in_city
flight_plan = flight_plan_to runway
carry_out flight_plan
end
def self.runways_in(city)
Airport.where(city: city).map(&:runways).flatten
end
def self.closest_available(runways)
runways.select { |r| r.available? }
.sort_by { |r| distance_between current_position, r.position }.last
end
def self.flight_plan_to(runway)
FlightPlan.new runway.latitude, runway.longitude
end
# other methods left to the imagination
end
Run Code Online (Sandbox Code Playgroud)
这是我编写的第一个测试它的rspec示例:
describe Autopilot
describe ".fly_to" do
it "flies to the only available runway" do
Autopilot.stub(:current_position) { Position.new 0, 0 }
nearby_runway = create :runway, latitude: 1, longitude: 1
create :runway, city: nearby_runway.city, latitude: 2, longitude: 2
flight_plan = FlightPlan.new nearby_runway.latitude, nearby_runway.longitude
# Think of the following line as being at the end of the example, since that's when it takes effect
Autopilot.should_receive(:carry_out).with flight_plan
Autopilot.fly_to nearby_runway.airport.city
end
end
end
Run Code Online (Sandbox Code Playgroud)
哦不 - 最后一行失败了这条消息:"期望失败:预期使用FlightPlan调用Autopilot.carry_out(纬度:1,经度:1),但是使用FlightPlan调用它(纬度:2,经度:2) ".我不知道那是怎么回事.我们最好使用Saff Squeeze.
内联方法(重命名本地以避免名称冲突):
it "flies to the only available runway" do
Autopilot.stub(:current_position) { Position.new 0, 0 }
nearby_runway = create :runway, latitude: 1, longitude: 1
create :runway, city: nearby_runway.city, latitude: 2, longitude: 2
flight_plan = FlightPlan.new nearby_runway.latitude, nearby_runway.longitude
Autopilot.should_receive(:carry_out).with flight_plan
runways_in_city = runways_in city
runway = closest_available runways_in_city
actual_flight_plan = flight_plan_to runway
Autopilot.carry_out actual_flight_plan
end
Run Code Online (Sandbox Code Playgroud)
我不知道最后一行怎么可能无法满足期望,只要它正确FlightPlan.让我们看看我们是否可以在测试中写出更高的失败断言:
it "flies to the only available runway" do
Autopilot.stub(:current_position) { Position.new 0, 0 }
nearby_runway = create :runway, latitude: 1, longitude: 1
create :runway, city: nearby_runway.city, latitude: 2, longitude: 2
flight_plan = FlightPlan.new nearby_runway.latitude, nearby_runway.longitude
Autopilot.should_receive(:carry_out).with flight_plan
runways_in_city = runways_in city
runway = closest_available runways_in_city
actual_flight_plan = flight_plan_to runway
actual_flight_plan.should == flight_plan
Autopilot.carry_out actual_flight_plan
end
Run Code Online (Sandbox Code Playgroud)
啊,新的断言也失败了,"预期的FlightPlan(纬度:1,经度:1),但得到了FlightPlan(纬度:2,经度:2)".好的,让我们简化测试:
it "flies to the only available runway" do
Autopilot.stub(:current_position) { Position.new 0, 0 }
nearby_runway = create :runway, latitude: 1, longitude: 1
create :runway, city: nearby_runway.city, latitude: 2, longitude: 2
flight_plan = FlightPlan.new nearby_runway.latitude, nearby_runway.longitude
runways_in_city = runways_in city
runway = closest_available runways_in_city
actual_flight_plan = flight_plan_to runway
actual_flight_plan.should == flight_plan
end
Run Code Online (Sandbox Code Playgroud)
我们到了某个地方,但我仍然没有看到什么是错的.更好的Saff再次挤压,内联flight_plan_to:
it "flies to the only available runway" do
Autopilot.stub(:current_position) { Position.new 0, 0 }
nearby_runway = create :runway, latitude: 1, longitude: 1
create :runway, city: nearby_runway.city, latitude: 2, longitude: 2
flight_plan = FlightPlan.new nearby_runway.latitude, nearby_runway.longitude
runways_in_city = runways_in city
runway = closest_available runways_in_city
actual_flight_plan = FlightPlan.new runway.latitude, runway.longitude
actual_flight_plan.should == flight_plan
end
Run Code Online (Sandbox Code Playgroud)
嗯,显然只要flight_plan_to得到正确的跑道就会通过.让我们断言:
it "flies to the only available runway" do
Autopilot.stub(:current_position) { Position.new 0, 0 }
nearby_runway = create :runway, latitude: 1, longitude: 1
create :runway, city: nearby_runway.city, latitude: 2, longitude: 2
flight_plan = FlightPlan.new nearby_runway.latitude, nearby_runway.longitude
runways_in_city = runways_in city
runway = closest_available runways_in_city
runway.should == nearby_runway
actual_flight_plan = FlightPlan.new runway.latitude, runway.longitude
actual_flight_plan.should == flight_plan
end
Run Code Online (Sandbox Code Playgroud)
好的,新的断言失败了,"预期的跑道(id:1)但是跑道(id:2)".再次简化测试:
it "flies to the only available runway" do
Autopilot.stub(:current_position) { Position.new 0, 0 }
nearby_runway = create :runway, latitude: 1, longitude: 1
create :runway, city: nearby_runway.city, latitude: 2, longitude: 2
runways_in_city = runways_in city
runway = closest_available runways_in_city
runway.should == nearby_runway
end
Run Code Online (Sandbox Code Playgroud)
我们已经将我们原来的测试和代码修剪到了显而易见的错误closest_available- 它应该使用first而不是last.
但是,如果它仍然不明显,你说呢?好吧,让我们再次尝试Saff Squeeze,内联closest_available:
it "flies to the only available runway" do
Autopilot.stub(:current_position) { Position.new 0, 0 }
nearby_runway = create :runway, latitude: 1, longitude: 1
create :runway, city: nearby_runway.city, latitude: 2, longitude: 2
runways_in_city = runways_in city
runway = runways_in_city.select { |r| r.available? }
.sort_by { |r| Autopilot.distance_between Autopilot.current_position, r.position }.last
runway.should == nearby_runway
end
Run Code Online (Sandbox Code Playgroud)
现在,我将在测试中将失败的断言置于何处?我不能 - 错误是在测试的最后一行.最终我会被迫意识到它在closest_available我内联之前已经存在.