Rspec ложно-положительный, потому что исключение сбоя спасено в тестируемом коде - PullRequest
2 голосов
/ 15 сентября 2011

У меня есть тест rspec, который я ожидаю провалить, но он проходит, потому что код, который он тестирует, спасает исключение, которое вызывает rspec. Вот пример ситуации:

class Thing do

  def self.method_being_tested( object )
    # ... do some stuff

    begin
      object.save!
    rescue Exception => e
      # Swallow the exception and log it
    end
  end

end

В файле rspec:

describe "method_being_tested" do
  it "should not call 'save!' on the object passed in" do
    # ... set up the test conditions

    mock_object.should_not_receive( :save! )
    Thing.method_being_tested( mock_object )
  end
end

Я знал, что казнь доходила до "object.save!" строка тестируемого метода, и поэтому тест должен быть неудачным, но тест проходит. Используя отладчик в блоке спасения, я нахожу следующее:

(rdb:1) p e # print the exception object "e"
#<RSpec::Mocks::MockExpectationError: (Mock "TestObject_1001").save!
    expected: 0 times
    received: 1 time>

То есть, в основном, тест не пройден, но он подавляется самим кодом, который он пытается протестировать. Я не могу найти жизнеспособный способ не дать этому коду проглотить исключения Rspec без какой-либо компрометации кода. Я не хочу, чтобы код явно проверял, является ли исключение исключением Rspec, потому что это плохой дизайн (тесты должны быть написаны для кода, код никогда не должен быть написан для тестов). Но я также не могу проверить, является ли исключение каким-либо конкретным типом, который я действительно хочу, чтобы он перехватывал, потому что я хочу, чтобы он перехватывал НИЧЕГО, что могло бы возникнуть в обычной производственной среде.

Кто-то, должно быть, имел эту проблему до меня! Пожалуйста, помогите мне найти решение.

Ответы [ 3 ]

4 голосов
/ 19 декабря 2013

Предполагая, что код правильный, как есть:

describe "method_being_tested" do
  it "should not call 'save!' on the object passed in" do
    # ... set up the test conditions
    calls = 0
    mock_object.stub(:save!) { calls += 1 }
    expect {Thing.method_being_tested(mock_object)}.to_not change{calls}
  end
end

Если нет необходимости перехватывать абсолютно все исключения, включая SystemExit, NoMemoryError, SignalException и т. Д. (Данные @ vito-botta):

begin
  object.save!
rescue StandardError => e
  # Swallow "normal" exceptions and log it
end

StandardError - это уровень исключения по умолчанию, перехваченный rescue.

1 голос
/ 20 июня 2013

Я бы сделал рефакторинг следующим образом:

class Thing do

  def self.method_being_tested!( object )

    # ... do some stuff

    return object.save
  end

end

Если вы хотите игнорировать исключение, созданное командой save!нет смысла звонить в save!на первом месте.Вы просто позвоните сохранить и сообщить код вызова соответственно.

1 голос
/ 16 сентября 2011

из rspec-mock:

module RSpec
  module Mocks
    class MockExpectationError < Exception
    end

    class AmbiguousReturnError < StandardError
    end
  end
end

Тебе действительно нужно поймать Exception? Не могли бы вы поймать StandardError вместо этого?

Поймать все исключения, как правило, плохо.

...