Forum: Rails Germany Private Methode in Funktionalem Test überprüfen

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
5d7c8b8f2de06c07b2bf0848d20ce71f?d=identicon&s=25 Christian Beier (cbeier)
on 2009-02-01 11:01
Hallo,

ich arbeite mich gerade in der Testgetriebenen Entwicklung ein und habe
noch etwas Startschwierigkeiten.

In einem Controller habe ich folgende (private) Methode angelegt:

  private
  def get_geocode(address)
    g = GeoKit::Geocoders::MultiGeocoder.geocode(address)
    return {:success => true, :lat => g.lat, :lng => g.lng, :city =>
g.city }
  end

Um die korrekte Funktion dieser Methode zu testen, habe ich in der Datei
*_controller_test.rb folgenden Test geschrieben:

  test "should get city from geocode" do
    geocode = "52.409645,13.059826"
    location = get_geocode(geocode)

    assert_equal "Potsdam", assigns(:location)
  end

Beim Ausführen des Tests, erhalte ich die Fehlermeldung:

 1) Error:
test_should_get_city_from_geocode(HotspotsControllerTest):
NoMethodError: undefined method `get_geocode' for
#<HotspotsControllerTest:0x243f5ec>


Kann ich in Funktionalen Tests überhaupt private Methoden testen?


Viele Grüße,
Christian
B5b39c8f21b5bb1ab97852ed32c888ab?d=identicon&s=25 Jan Krutisch (halfbyte)
on 2009-02-01 14:00
(Received via mailing list)
Hallo,

Am 01.02.2009 um 11:01 schrieb Christian Beier:

>  test "should get city from geocode" do
>    geocode = "52.409645,13.059826"
>    location = get_geocode(geocode)
>
>    assert_equal "Potsdam", assigns(:location)
>  end

> Beim Ausführen des Tests, erhalte ich die Fehlermeldung:
>
> 1) Error:
> test_should_get_city_from_geocode(HotspotsControllerTest):
> NoMethodError: undefined method `get_geocode' for
> #<HotspotsControllerTest:0x243f5ec>
>
>
> Kann ich in Funktionalen Tests überhaupt private Methoden testen?

Ich sehe 2 Probleme. Zum einen rufst Du die Methode ja gar nicht auf
dem Controller-Objekt auf (das wäre dann eher
@controller.get_geocode()) und zum anderen ist die Methode wie Du
anmerktest private. Das kann man theoretisch umgehen:

@controller.send(:get_geocode, geocode)

Allerdings ist das ein typischer Code-Smell. Grundsätzlich würde ich 2
Dinge dazu anmerken (Wenn Sie erlauben, Sir):

- private Methoden in funktionalen Tests abtesten zu wollen ist ein
Code Smell. Funktionale Tests sollten (so sehe ich das) den Controller
"von aussen" testen. Es sind eben KEINE Unit-Tests. Du müsstest also
eher die Actions, die diese Methode verwenden auf ihre richtige
Funktion testen.

- Das ganze riecht allerdings danach als wäre die Methode
grundsätzlich im Controller falsch aufgehoben, da es sich vermutlich
um Domänen-Logik (klingt furchtbar auf deutsch, finde ich) handelt,
die üblicherweise ins Modell gehört. Wenn ich Deinen Code richtig aus
Deinen Schnippseln extrapoliere klingt das alles danach als würdest Du
eigentlich auf Deinem Modell ein virtuelles Attribute "geocode"
anlegen wollen, was genau das macht, was bei Dir die Methode macht.

Hoffe gehulfen zu haben und

Gruß,

Jan
5d7c8b8f2de06c07b2bf0848d20ce71f?d=identicon&s=25 Christian Beier (cbeier)
on 2009-02-01 15:43
Jan Krutisch wrote:
> - private Methoden in funktionalen Tests abtesten zu wollen ist ein
> Code Smell. Funktionale Tests sollten (so sehe ich das) den Controller
> "von aussen" testen. Es sind eben KEINE Unit-Tests. Du m�sstest also
> eher die Actions, die diese Methode verwenden auf ihre richtige
> Funktion testen.

Ja, du hast natürlich recht. Ich wollte mit dem Test erst einmal
"kleinteilig" die korrekte Funktion bzw. korrekten Rückgabewert testen.
Da der Kram aber eh ins Model gehört, kann das ja über Unit Tests
erledigt werden.

Wie gesagt, ich beschäftige mich erst frisch mit Test und Rails
überhaupt. Im Moment muss ich auch erst noch herausfinden wie fein das
Testnetz geschrieben werden sollte, damit es a) nicht unnötig viele
werden und b) Sachen ungetestet bleiben und sich so doch Fehler
einschleichen können. So eine richtig gute Abhandlung die die
Testgetriebene Entwicklung in allen Bereichen behandelt (und auch das
Zusammenspiel), habe ich leider noch nicht gefunden.

> - Das ganze riecht allerdings danach als w�re die Methode
> grunds�tzlich im Controller falsch aufgehoben, da es sich vermutlich
> um Dom�nen-Logik (klingt furchtbar auf deutsch, finde ich) handelt,
> die �blicherweise ins Modell geh�rt. Wenn ich Deinen Code richtig aus
> Deinen Schnippseln extrapoliere klingt das alles danach als w�rdest Du
> eigentlich auf Deinem Modell ein virtuelles Attribute "geocode"
> anlegen wollen, was genau das macht, was bei Dir die Methode macht.

Ich dachte mir schon, das die Mehode im Controller nicht wirklich gut
aufgehoben ist. Es geht mir in der Methode grundsätzlich darum, weitere
Parameter zu einer Ortsangabe zu erhalten. In diesem Fall also aus
Geokoordinaten, möchte ich den Ort erhalten, um diesen in einem View
auszugeben.

Einfach die geschriebene Methode

  def get_geocode(address)
    g = MultiGeocoder.geocode(address)
    return {:success => true, :lat => g.lat, :lng => g.lng, :city =>
g.city }
  end

in das Model zu packen funktioniert ja (wohl) nicht. Zumindest bekomme
ich es so nicht vom Controller aus angesprochen (Hotspot.get_geocode) ->
NoMethodError: undefined method `get_geocode' for #<Class:0x243b438>


Grüße,
Christian
858485a2cf8b17a24a68c1e224ff77bd?d=identicon&s=25 Benjamin Behr | mindmatters (Guest)
on 2009-02-01 15:50
(Received via mailing list)
Hallo,

wenn du die Methode "get_geocode" auf der Klasse "Hotspot" rufen
möchtest, solltest du die Methode mit einem vorangeschriebenen "self."
definieren, damit sie im Klassen-Kontext ausgeführt werden kann. Also
sowas in der Art:

def self.get_geocode(address)
    g = MultiGeocoder.geocode(address)
    return {:success => true, :lat => g.lat, :lng => g.lng, :city =>
g.city }
  end

Schöne Grüße,
Benjamin

Am 01.02.2009 um 15:43 schrieb Christian Beier:
A23eb58a78fec3f6217ad5aa50657fac?d=identicon&s=25 Sven Klever (Guest)
on 2009-02-01 16:04
(Received via mailing list)
Hallo Christian,


>  So eine richtig gute Abhandlung die die
> Testgetriebene Entwicklung in allen Bereichen behandelt (und auch das
> Zusammenspiel), habe ich leider noch nicht gefunden.

unter folgender URL hab ich gestern ein PDF entdeckt, welches dir beim
Einsteig in TDD bestimmt weiterhelfen kann.

http://www.railsprescriptions.com/

Viele
Grüße,Sven
5d7c8b8f2de06c07b2bf0848d20ce71f?d=identicon&s=25 Christian Beier (cbeier)
on 2009-02-01 18:13
Benjamin Behr | mindmatters wrote:
> wenn du die Methode "get_geocode" auf der Klasse "Hotspot" rufen
> möchtest, solltest du die Methode mit einem vorangeschriebenen "self."
> definieren, damit sie im Klassen-Kontext ausgeführt werden kann. Also
> sowas in der Art:
>
> def self.get_geocode(address)
>     g = MultiGeocoder.geocode(address)
>     return {:success => true, :lat => g.lat, :lng => g.lng, :city =>
> g.city }
>   end
>
> Schöne Grüße,
> Benjamin

Vielen Dank für die Hilfe. Jetzt habe ich wieder was gelernt ;)


Sven Klever wrote:
> Hallo Christian,
>
>
>>  So eine richtig gute Abhandlung die die
>> Testgetriebene Entwicklung in allen Bereichen behandelt (und auch das
>> Zusammenspiel), habe ich leider noch nicht gefunden.
>
> unter folgender URL hab ich gestern ein PDF entdeckt, welches dir beim
> Einsteig in TDD bestimmt weiterhelfen kann.
>
> http://www.railsprescriptions.com/
>
> Viele
> Gr��e,Sven

Danke für den Tipp. Ich habe mir gerade das PDF heruntergeladen und
werde es mir mal in Ruhe durchlesen. Es sieht aber schon sehr hilfreich
aus und wird die noch offenen Fragen klären und mir etwas mehr
Sicherheit darüber geben, was in Tests getestet werden sollte.


Viele Grüße,
Christian
This topic is locked and can not be replied to.