Каков наилучший способ юнит-тестирования защищенных и приватных методов в Ruby? - PullRequest
128 голосов
/ 06 ноября 2008

Каков наилучший способ модульного тестирования защищенных и закрытых методов в Ruby с использованием стандартной среды Ruby Test::Unit?

Я уверен, что кто-то придумает и догматично скажет, что «вы должны только тестировать общедоступные методы, если требуется модульное тестирование, это не должен быть защищенный или закрытый метод», но меня это не очень интересует. обсуждая это. У меня есть несколько методов, которые являются защищенными или частными по уважительным и уважительным причинам, эти частные / защищенные методы умеренно сложны, и публичные методы в классе зависят от того, работают ли эти защищенные / частные методы правильно, поэтому Мне нужен способ проверить защищенные / приватные методы.

Еще одна вещь ... Обычно я помещаю все методы для данного класса в один файл, а модульные тесты для этого класса - в другой файл. В идеале я хотел бы, чтобы вся магия реализовала эту функциональность «модульного теста защищенных и закрытых методов» в файле модульного теста, а не в основном исходном файле, чтобы сделать основной исходный файл максимально простым и понятным.

Ответы [ 16 ]

2 голосов
/ 14 мая 2015

В Test :: Unit framework можно написать,

MyClass.send(:public, :method_name)

Здесь "method_name" - это закрытый метод.

& при вызове этого метода можно написать,

assert_equal expected, MyClass.instance.method_name(params)
2 голосов
/ 06 ноября 2008

Я бы, вероятно, склонялся к использованию instance_eval (). Однако прежде чем я узнал о instance_eval (), я бы создал производный класс в своем файле модульного теста. Затем я бы установил закрытый метод (ы) как public.

В приведенном ниже примере метод build_year_range является закрытым в классе PublicationSearch :: ISIQuery. Получение нового класса только для целей тестирования позволяет мне устанавливать методы, которые будут общедоступными и, следовательно, непосредственно тестируемыми. Аналогичным образом, производный класс предоставляет переменную экземпляра с именем 'result', которая ранее не отображалась.

# A derived class useful for testing.
class MockISIQuery < PublicationSearch::ISIQuery
    attr_accessor :result
    public :build_year_range
end

В моем модульном тесте у меня есть тестовый пример, который создает экземпляр класса MockISIQuery и напрямую тестирует метод build_year_range ().

1 голос
/ 02 марта 2010

Чтобы исправить верхний ответ выше: в Ruby 1.9.1 это Object # send, который отправляет все сообщения, и Object # public_send, который уважает конфиденциальность.

1 голос
/ 06 января 2010

Вместо obj.send вы можете использовать одноэлементный метод. Это еще 3 строки кода в вашем тестовый класс и не требует никаких изменений в реальном коде для тестирования.

def obj.my_private_method_publicly (*args)
  my_private_method(*args)
end

В тестовых случаях вы затем используете my_private_method_publicly всякий раз, когда хотите проверить my_private_method.

http://mathandprogramming.blogspot.com/2010/01/ruby-testing-private-methods.html

obj.send для частных методов был заменен на send! в 1.9, но позже send! был удален снова. Так что obj.send работает отлично.

1 голос
/ 25 ноября 2008

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

class Class
  def publicize_methods
    saved_private_instance_methods = self.private_instance_methods
    self.class_eval { public *saved_private_instance_methods }
    begin
      yield
    ensure
      self.class_eval { private *saved_private_instance_methods }
    end
  end
end

MyClass.publicize_methods do
  assert_equal 10, MyClass.new.secret_private_method
end

Использование отправки для доступа к защищенным / закрытым методам - это с пробелом в 1.9, поэтому это не рекомендуемое решение.

0 голосов
/ 01 апреля 2015

Для этого:

disrespect_privacy @object do |p|
  assert p.private_method
end

Вы можете реализовать это в своем файле test_helper:

class ActiveSupport::TestCase
  def disrespect_privacy(object_or_class, &block)   # access private methods in a block
    raise ArgumentError, 'Block must be specified' unless block_given?
    yield Disrespect.new(object_or_class)
  end

  class Disrespect
    def initialize(object_or_class)
      @object = object_or_class
    end
    def method_missing(method, *args)
      @object.send(method, *args)
    end
  end
end
...