什么是'Saff Squeeze'找到错误的方法?

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我内联之前已经存在.