Die Programmiersprache Ruby

Blog|

Forum|

Wiki  


Alle Zeiten sind UTC + 1 Stunde [ Sommerzeit ]

Ein neues Thema erstellen Auf das Thema antworten  [ 7 Beiträge ] 
Autor Nachricht
 Betreff des Beitrags: test/unit: Prüfen auf stdout
BeitragVerfasst: 03 Mai 2008, 22:18 
Offline
Son-shi

Registriert: 23 Feb 2004, 14:59
Beiträge: 941
Wohnort: Esslingen
Hi,

ich versuche immer noch in Unittests tiefer einzusteigen. Jetzt würde ich gerne mittels Unittests die Korrektheit von Bildschirmausgaben prüfen. Meine assertion sollte etwas in der Art sein:



    assert_stdout("xx\n", puts 'xx' )


Im Prinzip habe ich eine Lösung gefunden.

Das Abfangen der Bildschirmausgabe kriegt man mit einem Umsetzten von $stdout und einem erweiterten String hin:


1
2
3
4
5
6
7
8
9
10
11
12
#Catch output
class MyIO < String
def write( cont )
self << cont
end
end #MyIO
puts 'vorher'
$stdout = output = MyIO.new #temporary copy
puts 'YY'
$stdout = STDOUT #back to original stdout
puts 'nachher'
puts output

ergibt:
Zitat:
vorher
nachher
YY


Auf der Basis dieser Logik konnte ich meine Testbeispiele aufbauen:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#Catch output
class MyIO < String
def write( cont )
self << cont
end
end #MyIO
class Test::Unit::TestCase
def catch_stdout( procedure )
$stdout = output = MyIO.new #temporary copy
procedure.call
$stdout = STDOUT
output
end
#Problem: actual bereits ausgef��hrt.
def assert_stdout( expected, actual, message = nil )
output = catch_stdout( actual )
full_message = build_message(message, "<?> expected in stdout, but was\n<?>.\n", expected, output)
assert_block( full_message ){ expected == output }
end
#
end

class MyTest < Test::Unit::TestCase
def test_1()
assert_stdout("xx\n", proc{ puts 'xx' } )
assert_equal("xx\n", catch_stdout(proc{ puts 'xx' }) )
end
end


Was mir jetzt nicht gefällt: Ich muß immer ein Proc-Objekt übergeben. Um das zu umgehen, habe ich zusätzlich eine Variante mit einem Block eingeführt:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#Catch output
class MyIO < String
def write( cont )
self << cont
end
end #MyIO
class Test::Unit::TestCase
def catch_stdout( procedure )
$stdout = output = MyIO.new #temporary copy
procedure.call
$stdout = STDOUT
output
end
#Problem: actual bereits ausgef��hrt.
def assert_stdout( expected, actual, message = nil )
output = catch_stdout( actual )
full_message = build_message(message, "<?> expected in stdout, but was\n<?>.\n", expected, output)
assert_block( full_message ){ expected == output }
end
def assert_stdout_block( expected, message = nil )
if block_given?
$stdout = output = MyIO.new #temporary copy
yield
$stdout = STDOUT
else
raise 'no block given'
output = '--no block given --'
end
full_message = build_message(message, "<?> expected in stdout, but was\n<?>.\n", expected, output)
assert_block( full_message ){ expected == output }
end
end

class MyTest < Test::Unit::TestCase
def test_1()
assert_stdout("xx\n", proc{ puts 'xx' } )
assert_equal("xx\n", catch_stdout(proc{ puts 'xx' }) )
assert_stdout_block("xx\n"){ puts 'xx' }
#~ assert_stdout_block("xx\n") -> error, block fehlt
end
end


Hat jemand eine andere Idee oder Verbesserungsvorschläge?

_________________
http://ruby.lickert.net/
http://gems.rubypla.net/


Nach oben
 Profil  
 
 Betreff des Beitrags: Re: test/unit: Prüfen auf stdout
BeitragVerfasst: 05 Mai 2008, 03:36 
Offline
ri
Benutzeravatar

Registriert: 08 Apr 2006, 17:03
Beiträge: 788
Wohnort: Berlin
knut hat geschrieben:
Hi,

ich versuche immer noch in Unittests tiefer einzusteigen. Jetzt würde ich gerne mittels Unittests die Korrektheit von Bildschirmausgaben prüfen. Meine assertion sollte etwas in der Art sein:



    assert_stdout("xx\n", puts 'xx' )


mir erschließt sich der Sinn noch nicht so ganz, warum du das überhaupt benötigst. Im obigen Beispiel testest du die Methode 'puts'. Warum? Die ist doch (hoffentlich) ausreichend getestet - eigentlich solltest du doch deinen eigenen Code testen ...

Gruß
-Thomas

_________________
"Programs must be written for people to read, and only incidentally
for machines to execute."
- H. Abelson and G. Sussman
(in "The Structure and Interpretation of Computer Programs)


Zuletzt geändert von thopre am 05 Mai 2008, 13:10, insgesamt 1-mal geändert.

Nach oben
 Profil  
 
 Betreff des Beitrags:
BeitragVerfasst: 05 Mai 2008, 08:38 
Offline
Hacker
Benutzeravatar

Registriert: 10 Jun 2004, 11:07
Beiträge: 437
Wohnort: Erde
Moinmoin,

vielleicht wäre es besser nicht 'puts' zu testen, sondern die Strings die es ausgibt zu überprüfen.
Dazu könntest du die Arguments die du 'puts' ('print' etc.) übergibst in Methoden packen und das dann testen.

Nun will ich nicht vorschlagen immer alle Ausgaben so zu machen! Wo in den Ausgaben noch vergleichsweise viel passiert (Berechnungen, Formatierungen...) würde ich das machen. Und wenn die Ausgabe an verschiedenen Stellen benutzt wird - "Don't repeat yourself (or others)" - (vielleicht kommt noch ein Interface dazu (Webservices, GUI)). Sonst eher nicht.

Frohes Schaffen

Zehnbambusgarten

_________________
I find defects, so you don't have to.


Nach oben
 Profil  
 
 Betreff des Beitrags:
BeitragVerfasst: 05 Mai 2008, 12:21 
Offline
Interpreter

Registriert: 29 Okt 2002, 14:25
Beiträge: 2137
Zehnbambusgarten hat geschrieben:
vielleicht wäre es besser nicht 'puts' zu testen, sondern die Strings die es ausgibt zu überprüfen.

Genau. Ein klassischer Fall für mocking der Ausgabe-Umgebung.

Schau Dir mal Mocha oder Flexmock an, die sind genau für solche Sachen gebaut. :)

Gruß
janfri

_________________
Ruby-Mine

"Simplicity is the ultimate sophistication." Leonardo da Vinci


Nach oben
 Profil  
 
 Betreff des Beitrags: Re: test/unit: Prüfen auf stdout
BeitragVerfasst: 05 Mai 2008, 13:27 
Offline
Son-shi

Registriert: 23 Feb 2004, 14:59
Beiträge: 941
Wohnort: Esslingen
Danke für die Hinweise.
thopre hat geschrieben:
mir erschließt sich der Sinn noch nicht so ganz, warum du das überhaupt benötigst. Im obigen Beispiel testest du die Methode 'puts'. Warum? Die ist doch (hoffentlich) ausreichend getestet - eigentlich solltest du doch deinen eigenen Code testen ...

Meine Anwendung protokolliert zum Teil was sie macht. Und jetzt wollte ich prüfen, ob mein Protokoll meinen Erwartungen entspricht. Oder anders: Meine Funktion liefert ein Ergebnis (das ich mit dem Unittest prüfe), aber gleichzeitig gebe ich Hinweise auf evtl. Probleme bei der Abarbeitung. Und da will ich prüfen, ob wirklich die Warnung kommt, die ich eigentlich erwarte.

Da ich z.Zt. mein Programm mit einen Logger umstelle habe ich da evtl. auch einen Ansatz (Log in eine datei abspeichern und das vergleichen, oder anderweitig das Log als "Ergebnis" sammeln). Aber mit dem Abfangen von stdout (evtl stderr) könnte es auch funktionieren.

Noch ist es eher etwas Spielerei mit test/unit, aber bevor ich mein Programm umbaue wollte ich mal schauen, ob das machbar ist.

Betreff Mocking: Prinzipiell korrekt, aber wenn das Mock-Objekt komplexer wird als das eigentlich zu testende Objekt dann frage ich mich, ob der Test noch sinnvoll ist.

_________________
http://ruby.lickert.net/
http://gems.rubypla.net/


Nach oben
 Profil  
 
 Betreff des Beitrags: Re: test/unit: Prüfen auf stdout
BeitragVerfasst: 05 Mai 2008, 16:15 
Offline
Interpreter

Registriert: 29 Okt 2002, 14:25
Beiträge: 2137
knut hat geschrieben:
aber wenn das Mock-Objekt komplexer wird als das eigentlich zu testende Objekt dann frage ich mich, ob der Test noch sinnvoll ist.

Dann hast Du irgendwas falsch gemacht. ;) Mocking soll es eigentlich vereinfachen und das tut es auch, wenn man es richtig macht.

Gruß
janfri

_________________
Ruby-Mine

"Simplicity is the ultimate sophistication." Leonardo da Vinci


Nach oben
 Profil  
 
 Betreff des Beitrags:
BeitragVerfasst: 05 Mai 2008, 16:34 
Offline
Son-shi

Registriert: 23 Feb 2004, 14:59
Beiträge: 941
Wohnort: Esslingen
Mal ein anderes hypothetisches Beispiel um zu sehen, wo man eine Prüfung auf Stdout verwenden könnte.
Angenommen ich habe meine eigene Division, die bei Division durch null als Ergbenis Null liefern soll, gleichzeitig aber eine Warnung ausgeben. Mein Unit Test wäre dann:


1
2
3
assert_equal(2.5, my_division(5,2) )
assert_equal(0, my_division(5,0) )
assert_stdout("Warning: Division by Zero. Set 0 as result\n", my_division(5,0) )


Bei mir sind das eher so Fälle, in denen ich Tabellen aufbaue, bei denen eigentlich keine leeren Zellen existieren. Aber möglich sind sie. Da gebe ich mir eine Warnung aus um einen Hinweis zu haben, das evtl. was mit den Daten nicht stimmt.

Mocking ist da, zumindest soweit ich das verstanden habe, fehl am Platz. Ich habe ja kein Problem meinen Testlauf zu erzeugen. Nur habe ich neben einem Ergebnis noch einen "Seiteneffekt", nämlich meine Ausgabe auf stdout.

_________________
http://ruby.lickert.net/
http://gems.rubypla.net/


Nach oben
 Profil  
 
Beiträge der letzten Zeit anzeigen:  Sortiere nach  
Ein neues Thema erstellen Auf das Thema antworten  [ 7 Beiträge ] 

Alle Zeiten sind UTC + 1 Stunde [ Sommerzeit ]


Wer ist online?

Mitglieder in diesem Forum: Google [Bot] und 1 Gast


Du darfst keine neuen Themen in diesem Forum erstellen.
Du darfst keine Antworten zu Themen in diesem Forum erstellen.
Du darfst deine Beiträge in diesem Forum nicht ändern.
Du darfst deine Beiträge in diesem Forum nicht löschen.
Du darfst keine Dateianhänge in diesem Forum erstellen.

Suche nach: