unit-testing - 查找错误的 'Saff Squeeze'方法到底是什么?

标签 unit-testing debugging language-agnostic tdd

我已经阅读了有关Saff Squeeze方法的肯特·贝克(Kent Beck)的原始博客post。我还阅读了this InfoQ post,它详细说明了该主题,但未提供任何示例。

我知道这实质上是一种不依赖调试器就可以发现错误的方法。但是我发现肯特的例子不清楚。

能否有一个更开明的人通过清晰明确的示例来教育我如何使用这种方法?希望它也可以作为其他学习该方法的人的学习资源。

最佳答案

Saff Squeeze是一种系统技术,可从失败的测试中删除测试代码和非测试代码,直到测试和代码小到可以理解为止。

我同意Kent's original description of the Saff Squeeze有点困难,部分是因为他正在测试的软件JUnit非常抽象,部分是因为他没有给出足够的步骤2的示例,“在测试中将(失败的)断言比现有的早断言。”

在他的第一轮中,他只是在测试中将断言上移,而他对后续步骤的总结可能会使您认为您在步骤2中唯一可以做的就是移动现有断言,但是到了最后一步,他想出了一个新的,更简单的失败断言。第2步中的断言可以只是在测试中上移的现有断言,这很常见,但也可以是随着对代码的理解和错误的发展而提出的新断言。

这是一个例子。需要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

这是我编写的第一个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

哦,不-最后一行失败,并显示以下消息:“期望失败:预期使用FlightPlan(纬度:1,经度:1)调用Autopilot.carry_out,但是使用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

我看不出最后一行是否能满足期望,只要它获得了正确的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

啊,新的断言也失败了,“预期的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

我们到了某个地方,但我仍然看不到有什么问题。更好的Saff Squeeze再次嵌入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

好吧,只要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

好的,新的断言失败,并带有“预期的Runway(id:1)但得到了Runway(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

我们已经将原始测试和代码修剪到明显的bug在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

现在,我将在测试中将失败的断言放在哪里更高呢?我不能-该错误位于测试的最后一行。最终,在内联之前,我将不得不意识到它已经在closest_available中了。

关于unit-testing - 查找错误的 'Saff Squeeze'方法到底是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23865274/

相关文章:

debugging - 在变量值更改时设置断点

java - 处理 AsyncCallback<T>?

security - 在受感染的机器上部署软件

regex - 匹配换行符 -\n 或\r\n?

ios - 如何对从服务器接收响应的代码进行单元测试

unit-testing - 在 iTextSharp 中测试代码

java - Mockito - 模拟子类方法调用

javascript - 如何使用 vue-test-utils 和 jest 在单元测试期间模拟 Vue Mixins?

java - Eclipse error(s)_during_the_evaluation for watch expression having quotes

algorithm - 获得最接近的字符串匹配