Ответ 1
Saff Squeeze - это систематическая методика удаления как тестового кода, так и не тестового кода из неудачного теста, пока тест и код не станут достаточно маленькими для понимания.
Я согласен с тем, что первоначальное описание Кента 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 when it takes effect
Autopilot.should_receive(:carry_out).with flight_plan
Autopilot.fly_to nearby_runway.airport.city
end
end
end
О нет - последняя строка завершается с этим сообщением: "Ожидание не выполнено: ожидается, что Autopilot.carry_out будет вызван с FlightPlan (широта: 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
Я не понимаю, как эта последняя строка может не соответствовать ожиданиям, если она получит правильный 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 Сожми flight_plan_to
раз, вставив 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
Хорошо, новое утверждение не выполнено, с "ожидаемой ВПП (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
Мы сократили наш исходный тест и код до такой степени, что становится очевидным, что ошибка находится в 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
прежде чем я добавлю это.