Что лучшим способом являются к модульному тесту защищенные и закрытые методы в Ruby?

Исключение нулевого указателя генерируется, когда приложение пытается использовать null в случае, когда требуется объект. К ним относятся:

  1. Вызов метода экземпляра объекта null.
  2. Доступ или изменение поля объекта null.
  3. Принимая длину null, как если бы это был массив.
  4. Доступ или изменение слотов null, как если бы это был массив.
  5. Бросок null как будто это было значение Throwable.

Приложения должны бросать экземпляры этого класса, чтобы указать на другие незаконные использования объекта null.

Ссылка: http://docs.oracle.com/javase/8/docs/api/java/lang/NullPointerException.html

132
задан Keith Pinson 21 December 2012 в 18:18
поделиться

11 ответов

Можно "вновь открыть" класс и предоставить новый метод, который делегирует к частному:

class Foo
  private
  def bar; puts "Oi! how did you reach me??"; end
end
# and then
class Foo
  def ah_hah; bar; end
end
# then
Foo.new.ah_hah
3
ответ дан 24 November 2019 в 00:09
поделиться

Одним путем я сделал это в прошлом:

class foo
  def public_method
    private_method
  end

private unless 'test' == Rails.env

  def private_method
    'private'
  end
end
9
ответ дан 24 November 2019 в 00:09
поделиться

Можно обойти инкапсуляцию с отправить методом:

myobject.send(:method_name, args)

Это - 'функция' Ruby. :)

были внутренние дебаты во время разработки Ruby 1.9, которая полагала, что наличие send относится с уважением к конфиденциальности, и send! игнорируют его, но в конце ничто не изменилось в Ruby 1.9. Проигнорируйте комментарии ниже обсуждения send! и повреждение вещей.

133
ответ дан 24 November 2019 в 00:09
поделиться

Просто вновь откройте класс в своем тестовом файле и переопределите метод или методы как общественность. Вы не должны переопределить кишки самого метода, просто передать символ в эти public вызов.

, Если Вы исходный класс определяетесь как это:

class MyClass

  private

  def foo
    true
  end
end

В Вас тестовый файл, просто сделайте что-то вроде этого:

class MyClass
  public :foo

end

можно передать несколько символов public, если Вы хотите выставить больше закрытых методов.

public :foo, :bar
31
ответ дан 24 November 2019 в 00:09
поделиться

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

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

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

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

2
ответ дан 24 November 2019 в 00:09
поделиться

instance_eval() мог бы помочь:

--------------------------------------------------- Object#instance_eval
     obj.instance_eval(string [, filename [, lineno]] )   => obj
     obj.instance_eval {| | block }                       => obj
------------------------------------------------------------------------
     Evaluates a string containing Ruby source code, or the given 
     block, within the context of the receiver (obj). In order to set 
     the context, the variable self is set to obj while the code is 
     executing, giving the code access to obj's instance variables. In 
     the version of instance_eval that takes a String, the optional 
     second and third parameters supply a filename and starting line 
     number that are used when reporting compilation errors.

        class Klass
          def initialize
            @secret = 99
          end
        end
        k = Klass.new
        k.instance_eval { @secret }   #=> 99

можно использовать его для закрытых методов доступа и переменных экземпляра непосредственно.

Вы могли также рассмотреть использование send(), который также предоставит Вам доступ к закрытым и защищенным методам (как предложенный James Baker)

, С другой стороны, Вы могли изменить метакласс своего тестового объекта обнародовать частные / защищенные методы, общедоступные только для того объекта.

    test_obj.a_private_method(...) #=> raises NoMethodError
    test_obj.a_protected_method(...) #=> raises NoMethodError
    class << test_obj
        public :a_private_method, :a_protected_method
    end
    test_obj.a_private_method(...) # executes
    test_obj.a_protected_method(...) # executes

    other_test_obj = test.obj.class.new
    other_test_obj.a_private_method(...) #=> raises NoMethodError
    other_test_obj.a_protected_method(...) #=> raises NoMethodError

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

10
ответ дан 24 November 2019 в 00:09
поделиться

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

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, так не рекомендуемое решение.

1
ответ дан 24 November 2019 в 00:09
поделиться

Вот один простой способ при использовании RSpec:

before(:each) do
  MyClass.send(:public, *MyClass.protected_instance_methods)  
end
71
ответ дан 24 November 2019 в 00:09
поделиться

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

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

У меня есть несколько методов, которые защищенный или частный навсегда и уважительные причины

Каковы эти веские причины? Другие языки ООП могут вообще обойтись без частных методов (на ум приходит smalltalk - где частные методы существуют только как соглашение).

7
ответ дан 24 November 2019 в 00:09
поделиться
[

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

] [
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[] для private-методов в 1.9 была заменена на []send![], но позже []send![] была снова удалена. Так что []obj.send[] работает отлично. [

]
1
ответ дан 24 November 2019 в 00:09
поделиться

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

1
ответ дан 24 November 2019 в 00:09
поделиться
Другие вопросы по тегам:

Похожие вопросы: