web-dev-qa-db-fra.com

Test de la méthode privée dans Ruby (rspec)

Oui, je sais, que tester des méthodes privées n'est pas une bonne idée (et j'ai lu ce fil - http://www.Ruby-forum.com/topic/197346 - et quelques autres)

Mais comment puis-je tester le code suivant?

J'utilise xmpp4r. Dans ma méthode publique #listen Je commence à recevoir des messages Jabber comme ceci:

def listen
  @client.add_message_callback do |m|
    do_things_with_message(m)
  end
end

private
def do_things_with_message(m)
  #
end

#add_message_callback - exécute le bloc, lorsque le message arrive (dans un fil différent)

Donc, tester #listen la méthode est difficile et teste plus xmpp4r que mon #do_things_with_message

Comment bien faire et tester #do_things_with_message? :) ( http://www.Ruby-forum.com/topic/197346#859664 )

Refactoriser les méthodes privées vers un nouvel objet l'essentiel serait que je les rende public (et classe avec une méthode - c'est insensé

EDIT: Il s'agit plus d'une question théorique sur le code propre et les tests corrects. Dans mon premier lien, les gens soutiennent que les méthodes privées de test sont mal. Je ne veux pas tricher avec #send, mais je ne vois aucun moyen viable de refactoriser

44
Andrey

Vous pouvez appeler une méthode privée dans Ruby en utilisant la méthode send . Quelque chose comme ceci:

@my_object = MyObject.new
@my_object.send(:do_things_with_message, some_message)

Dans un test qui ressemblerait à quelque chose comme:

it "should do a thing" do
  my_object = MyObject.new
  my_object.send(:do_things_with_message, some_message)
  my_object.thing.should == true
end
91
Andrew Hubbs

En laissant de côté la question de savoir si vous devriez tester une méthode privée, il est très possible dans Ruby pour rendre temporairement une méthode privée publique. Voici ce que je veux dire:

# Metaprogrammatical magic to temporarily expose
# a Class' privates (methods).
class Class
  def publicize_methods
    saved_private_instance_methods = self.private_instance_methods
    self.class_eval { public *saved_private_instance_methods }
    yield
    self.class_eval { private *saved_private_instance_methods }
  end
end

Vous utiliseriez publicize_methods comme ça:

ClassToTest.publicize_methods do
  ...
  do_private_things_with_message(m).should ???
  ...
end
13
buruzaemon

Vous avez probablement entendu cette pensée dans toutes les ressources que vous mentionnez, mais la bonne façon "théorique" de le faire serait de tester que @client reçoit add_message_callback, puis testez indirectement vos méthodes privées avec des tests d'intégration. L'intérêt des tests unitaires est que vous pouvez modifier l'implémentation, et vos tests réussiront toujours

3
enthrops