Подтвердить что ты не робот

Rails: Как написать тесты для модуля ruby?

Я хотел бы знать, как писать модульные тесты для модуля, который смешивается с несколькими классами, но не знает, как это сделать:

  • Я могу проверить методы экземпляра, написав тесты в одном из тестовых файлов для класса, который включает их (это не кажется правильным), или вы можете как-то сохранить тесты для включенных методов в отдельный файл к модулю?

  • Тот же вопрос относится к методам класса.

  • Должен ли я иметь отдельный тестовый файл для каждого из классов в модуле, например, модели обычных рельсов, или они живут в общем тестовом файле модуля, если это существует?

4b9b3361

Ответ 1

IMHO, вы должны выполнять функциональное покрытие тестирования, которое будет охватывать все применения модуля, а затем протестировать его изолированно в unit test:

setup do
  @object = Object.new
  @object.extend(Greeter)
end

should "greet person" do
  @object.stubs(:format).returns("Hello {{NAME}}")
  assert_equal "Hello World", @object.greet("World")
end

should "greet person in pirate" do
  @object.stubs(:format).returns("Avast {{NAME}} lad!")
  assert_equal "Avast Jim lad!", @object.greet("Jim")
end

Если ваши модульные тесты хороши, вы должны иметь возможность smoke test использовать функциональность в модулях, в которые она была помещена.

Или...

Напишите тестовый помощник, который утверждает правильное поведение, а затем примените его к каждому классу, который он смешивал. Использование было бы следующим:

setup do
  @object = FooClass.new
end

should_act_as_greeter

Если ваши тесты на устройства хороши, это может быть простой smoke test ожидаемого поведения, проверка правильных делегатов вызывается и т.д.

Ответ 2

Используйте встроенные классы (я не делаю никаких причудливых флешмоков или использования stubba/mocha, чтобы показать точку)

def test_should_callout_to_foo
   m = Class.new do
     include ModuleUnderTest
     def foo
        3
     end
   end.new
   assert_equal 6, m.foo_multiplied_by_two
 end

Любая издевательская/обучающая библиотека там должна дать вам более чистый способ сделать это. Также вы можете использовать structs:

 instance = Struct.new(:foo).new
 class<<instance
     include ModuleUnderTest
 end
 instance.foo = 4

Если у меня есть модуль, который используется во многих местах, у меня есть unit test для него, который делает именно это (слайд тестового объекта в соответствии с методами модуля и проверьте, правильно ли работают методы модуля на этом объекте).

Ответ 3

Я стараюсь, чтобы мои тесты были сосредоточены только на контракте для этого конкретного класса/модуля. Если я доказал поведение модуля в тестовом классе для этого модуля (обычно, включив этот модуль в тестовый класс, объявленный в спецификации для этого модуля), я не буду дублировать этот тест для производственного класса, который использует этот модуль. Но если есть дополнительное поведение, которое я хочу проверить для производственного класса или проблемы интеграции, я напишу тесты для производственного класса.

Например, у меня есть модуль под названием AttributeValidator, который выполняет облегченные проверки, похожие на ActiveRecord. Я пишу тесты для поведения модуля в спецификации модуля:

before(:each) do
  @attribute_validator = TestAttributeValidator.new
end

describe "after set callbacks" do
  it "should be invoked when an attribute is set" do
    def @attribute_validator.after_set_attribute_one; end
    @attribute_validator.should_receive(:after_set_attribute_one).once
    @attribute_validator.attribute_one = "asdf"
  end
end

class TestAttributeValidator 
    include AttributeValidator
    validating_str_accessor [:attribute_one, /\d{2,5}/]      
end

Теперь в производственном классе, который включает модуль, я не буду повторно утверждать, что обратные вызовы сделаны, но я могу утверждать, что включенный класс имеет определенный набор проверки с определенным регулярным выражением, что-то особенное для этого класса, но не воспроизводя тесты, которые я написал для модуля. В спецификации для производственного класса я хочу гарантировать, что определенные проверки будут установлены, но не то, что валидации работают в целом. Это своего рода интеграционный тест, но тот, который не повторяет те же утверждения, которые я сделал для модуля:

describe "ProductionClass validation" do
  it "should return true if the attribute is valid" do
    @production_class.attribute = @valid_attribute 
    @production_class.is_valid?.should be_true
  end
  it "should return false if the attribute is invalid" do
    @production_class.attribute = @invalid_attribute
    @production_class.is valid?.should be_false
  end
end

Здесь существует некоторое дублирование (как и большинство тестов интеграции), но тесты доказывают мне две разные вещи. Один набор тестов доказывает общее поведение модуля, а другой - конкретные проблемы реализации производственного класса, который использует этот модуль. Из этих тестов я знаю, что модуль будет проверять атрибуты и выполнять обратные вызовы, и я знаю, что мой производственный класс имеет определенный набор валидаций для определенных критериев, уникальных для производственного класса.

Надеюсь, что это поможет.

Ответ 4

Я обычно тестировал бы модуль как можно больше изоляции, по существу тестируя методы, с достаточным количеством кода, mocks и stubs, чтобы заставить его работать.

Тогда у меня, вероятно, также были бы тесты для классов, в которые были включены модули. Я не могу тестировать каждый класс, но тестировал бы достаточное количество классов, чтобы получить хороший охват и получить представление о любых возникающих проблемах. Этим испытаниям не требуется явно тестировать модуль, но, безусловно, проверять его использование в конкретных сценариях.

Каждый набор тестов будет иметь свой собственный файл.