Was ist der beste Weg, um geschützte und private Methoden in Ruby unter Verwendung des Standard-Ruby- Test::Unit
Frameworks zu testen ?
Ich bin sicher, jemand wird sich melden und dogmatisch behaupten, dass "Sie nur öffentliche Methoden testen sollten; wenn es Unit-Tests benötigt, sollte es keine geschützte oder private Methode sein", aber ich bin nicht wirklich daran interessiert, darüber zu debattieren. Ich habe mehrere Methoden, die aus guten und gültigen Gründen geschützt oder privat sind. Diese privaten / geschützten Methoden sind mäßig komplex, und die öffentlichen Methoden in der Klasse hängen davon ab, dass diese geschützten / privaten Methoden ordnungsgemäß funktionieren. Daher brauche ich eine Möglichkeit zum Testen die geschützten / privaten Methoden.
Noch etwas ... Ich habe im Allgemeinen alle Methoden für eine bestimmte Klasse in einer Datei abgelegt und die Komponententests für diese Klasse in einer anderen Datei. Idealerweise möchte ich die ganze Magie, diese "Unit-Test von geschützten und privaten Methoden" -Funktionalität in die Unit-Test-Datei und nicht in die Hauptquelldatei zu implementieren, um die Hauptquelldatei so einfach und unkompliziert wie möglich zu halten.
quelle
Antworten:
Sie können die Kapselung mit der Sendemethode umgehen:
Dies ist eine 'Funktion' von Ruby. :) :)
Während der Entwicklung von Ruby 1.9 gab es interne Debatten, in denen erwogen wurde,
send
die Privatsphäre zu respektieren und zusend!
ignorieren, aber am Ende änderte sich in Ruby 1.9 nichts. Ignorieren Sie die folgenden Kommentaresend!
, um Dinge zu besprechen und zu brechen.quelle
send!
Sache, die vor langer Zeit widerrufen wurde,send/__send__
kann Methoden aller Sichtbarkeit aufrufenpublic_send
(Dokumentation hier ), wenn Sie die Privatsphäre respektieren möchten. Ich denke, das ist neu in Ruby 1.9.Hier ist eine einfache Möglichkeit, wenn Sie RSpec verwenden:
quelle
Öffnen Sie einfach die Klasse in Ihrer Testdatei erneut und definieren Sie die Methode oder Methoden als öffentlich neu. Sie müssen den Mut der Methode selbst nicht neu definieren, sondern nur das Symbol an den
public
Aufruf übergeben.Wenn Ihre ursprüngliche Klasse wie folgt definiert ist:
Führen Sie in Ihrer Testdatei einfach Folgendes aus:
Sie können mehrere Symbole übergeben,
public
wenn Sie mehr private Methoden verfügbar machen möchten.quelle
instance_eval()
könnte helfen:Sie können damit direkt auf private Methoden und Instanzvariablen zugreifen.
Sie können auch die Verwendung in Betracht ziehen, mit
send()
der Sie auch auf private und geschützte Methoden zugreifen können (wie von James Baker vorgeschlagen).Alternativ können Sie die Metaklasse Ihres Testobjekts ändern, um die privaten / geschützten Methoden nur für dieses Objekt öffentlich zu machen.
Auf diese Weise können Sie diese Methoden aufrufen, ohne andere Objekte dieser Klasse zu beeinflussen. Sie können die Klasse in Ihrem Testverzeichnis erneut öffnen und für alle Instanzen in Ihrem Testcode öffentlich machen. Dies kann sich jedoch auf Ihren Test der öffentlichen Schnittstelle auswirken.
quelle
Eine Möglichkeit, wie ich es in der Vergangenheit gemacht habe, ist:
quelle
Sie können diese auch in ein neues Objekt umgestalten, in dem diese Methoden öffentlich sind, und sie in der ursprünglichen Klasse privat an sie delegieren. Auf diese Weise können Sie die Methoden ohne magische Metarubie in Ihren Spezifikationen testen und sie dennoch privat halten.
Was sind diese triftigen Gründe? Andere OOP-Sprachen können überhaupt ohne private Methoden auskommen (Smalltalk fällt mir ein - wo private Methoden nur als Konvention existieren).
quelle
Ähnlich wie bei der Antwort von @ WillSargent habe ich Folgendes in einem
describe
Block für den speziellen Fall des Testens einiger geschützter Validatoren verwendet, ohne den Schwergewichtsprozess des Erstellens / Aktualisierens mit FactoryGirl durchlaufen zu müssen (und Sie könnten esprivate_instance_methods
ähnlich verwenden):quelle
Um alle geschützten und privaten Methoden für die beschriebene Klasse öffentlich zu machen, können Sie Ihrer spec_helper.rb Folgendes hinzufügen, ohne eine Ihrer Spezifikationsdateien berühren zu müssen.
quelle
Sie können die Klasse "erneut öffnen" und eine neue Methode bereitstellen, die an die private delegiert:
quelle
Ich würde mich wahrscheinlich dazu neigen, instance_eval () zu verwenden. Bevor ich jedoch von instance_eval () wusste, erstellte ich eine abgeleitete Klasse in meiner Unit-Test-Datei. Ich würde dann die private (n) Methode (n) als öffentlich festlegen.
Im folgenden Beispiel ist die Methode build_year_range in der Klasse PublicationSearch :: ISIQuery privat. Durch das Ableiten einer neuen Klasse nur zu Testzwecken kann ich festlegen, dass eine oder mehrere Methoden öffentlich und daher direkt testbar sind. Ebenso macht die abgeleitete Klasse eine Instanzvariable namens 'result' verfügbar, die zuvor nicht verfügbar gemacht wurde.
In meinem Komponententest habe ich einen Testfall, der die MockISIQuery-Klasse instanziiert und die build_year_range () -Methode direkt testet.
quelle
Ich weiß, dass ich zu spät zur Party komme, aber teste keine privaten Methoden ... Ich kann mir keinen Grund dafür vorstellen. Eine öffentlich zugängliche Methode verwendet diese private Methode irgendwo, testet die öffentliche Methode und die verschiedenen Szenarien, die dazu führen würden, dass diese private Methode verwendet wird. Etwas geht rein, etwas kommt raus. Das Testen privater Methoden ist ein großes Nein-Nein, und es macht es viel schwieriger, Ihren Code später umzugestalten. Sie sind aus einem bestimmten Grund privat.
quelle
In Test :: Unit kann Framework schreiben,
Hier ist "Methodenname" eine private Methode.
& beim Aufrufen dieser Methode kann schreiben,
quelle
Hier ist eine allgemeine Ergänzung zu Class, die ich benutze. Es ist ein bisschen mehr Schrotflinte als nur die Methode, die Sie testen, öffentlich zu machen, aber in den meisten Fällen spielt es keine Rolle und es ist viel besser lesbar.
Die Verwendung von send für den Zugriff auf geschützte / private Methoden ist in Version 1.9 fehlerhaft und wird daher nicht empfohlen.
quelle
Um die obige Top-Antwort zu korrigieren: In Ruby 1.9.1 sendet Object # send alle Nachrichten und Object # public_send respektiert die Privatsphäre.
quelle
Anstelle von obj.send können Sie eine Singleton-Methode verwenden. Es sind 3 weitere Codezeilen in Ihrer Testklasse und es sind keine Änderungen am tatsächlich zu testenden Code erforderlich.
In den Testfällen verwenden Sie dann,
my_private_method_publicly
wann immer Sie testen möchtenmy_private_method
.http://mathandprogramming.blogspot.com/2010/01/ruby-testing-private-methods.html
obj.send
für private Methoden wurdesend!
in 1.9 ersetzt, aber spätersend!
wieder entfernt. Funktioniert alsoobj.send
einwandfrei.quelle
Um dies zu tun:
Sie können dies in Ihrer Datei test_helper implementieren:
quelle
Disrespect
inPrivacyViolator
(: P) und diedisrespect_privacy
Methode hat die Bindung des Blocks vorübergehend bearbeitet, um das Zielobjekt an das Wrapper-Objekt zu erinnern, aber nur für die Dauer des Blocks. Auf diese Weise müssen Sie keinen Blockparameter verwenden. Sie können einfach weiter auf das gleichnamige Objekt verweisen.