Guten Abend,
ich habe ein C-Projekt (Ansi C), das aus mehreren .c und .h Dateien
besteht. Die C-Funktionen in diesem Projekt tauschen untereinander Daten
aus, so dass es Eingangs- und Ausgangswerte gibt, die auf Korrektheit
überprüft werden können.
Vereinfacht führt eine C-Funktions eine Addition von input1 und input2
mit folgendem Testcode durch:
Schreibe: input1=1; input2=2;
Lese: output=x;
Vergleiche: output==3?
Frage: Mit welchem Ansatz (Script, Tool, Software-Debugger etc.) würde
man diese Aufgabe lösen, sprich wo läuft der Testcode und wo der
vorhandene C-Code?
Dino.
Dino schrieb:> Dieses ist ein Black-Box Test, sprich Daten gehen rein und raus.> Bei Unit-Tests handelt es sich per Definition um White-Box Tests.
Das stimmt nicht, sry. Unit Tests sind Black-Box tests.
Rene H. schrieb:> Das stimmt nicht, sry. Unit Tests sind Black-Box tests.
Unter Modultests verstehe ich Zweigüberdeckung etc. und hier möchte ich
ja auch Modulübergreifend testen, also nicht nur ein Modul.
Aber bei Wikipedia habe ich jetzt auch gelesen, dass ein Modultests
testen gemäß dem Design-by-contract-Prinzip auch ein Black-Box Test ist.
Ok.
Beispielsweise übergebe ich argumente in den Schnittstellen-Funktionen
und bekomme am Ende irgendwo ein Return-Value in einem anderen Modul
(wie ist das überhaupt definiert?) als Ergebnis zurück, wie wäre das
einer Testart zuzuordnen?
Dino schrieb:> Unter Modultests verstehe ich Zweigüberdeckung etc. und hier möchte ich> ja auch Modulübergreifend testen, also nicht nur ein Modul.
Das ist ziemlich egal, wieviel da drin hängt, solange der
Einsprungspunkt dem Rückgabepunkt entspricht.
Rene H. schrieb:> Das ist ziemlich egal, wieviel da drin hängt, solange der> Einsprungspunkt dem Rückgabepunkt entspricht.
und wie/womit würde man das testen?
Auf dem Internet wirst Du sicher welche finden. Meine kann ich leider
nicht zur Verfügung stellen.
Schau dir mal Nose an, dass verwende ich. Da gibts auch viele gute
Beispiele.
Dino schrieb:> Frage: Mit welchem Ansatz (Script, Tool, Software-Debugger etc.) würde> man diese Aufgabe lösen, sprich wo läuft der Testcode und wo der> vorhandene C-Code?
C ist keine Interpretersprache, man kann also interne Funktionen nicht
einfach per Testscript aufrufen.
Der Testscript muss also in C geschrieben und hinzukompiliert werden,
als eigenständig lauffähiges Testprogramm.
Statt main des eigentlichen Programms wird also main des
Testrahmenprogramms aufgerufen, in dem so etwas wie:
1
test.c:
2
int main(void)
3
{
4
input1=1;
5
input2=2;
6
output=Testfunktion(input1,input2);
7
assert(output==3);
8
}
Da assert das Programm bei Fehler unter Nennung einer Zeilennummer
abbricht, kann ein if mit printf nützlicher sein: Dann werden auch die
nachfolgenden Tests ausgeführt.
Gegen Fehler im Testprogramm selber hilft try catch.
CppCon 2015: Matt Hargett “Advanced Unit Testing in C & C++”
https://youtu.be/Wmy6g-aVgZI?list=PLHTh1InhhwT75gykhs7pqcR_uSiG601oh
cppunit
https://freedesktop.org/wiki/Software/cppunit/
cgreen
https://github.com/cgreen-devs/cgreenDino schrieb:> Ich verstehe noch nicht, warum ich ein Testframework brauche
Brauchen tut man das nicht. Aber : Du bist nicht der Erste mit diesem
Problem, und auch nicht der Letzte. Und da haben sich schon mal tausende
von Entwicklern weltweit gedanken zu gemacht. Dadurch sind halt
Werkzeuge entstanden, Testframeworks, die mittlerweile ebenfalls
tausendfach erprobt sind und die sich bewährt haben.
Warum willst du das Rad selber neu erfinden, und in all die Fallen
laufen, in die schon mal andere gelaufen sind, die die Fallen dann aus
dem Weg geräumt haben?
Dino schrieb:> Was sind die Vorteile davon?
Du hast 5000 Test, und alle sind vom Ablauf und der Syntax gleich.
verstehst du einen Test, verstehst du alle 5000.
Bastelst du dir selber was, hast du nachher 5000 verschiedene Abläufe,
alle mit anderer Syntax, und jemand (inklusive dir selbst!), der sich
deine Test später mal anschaut, muss bei jedem Test wieder ganz von
vorne anfangen zu überlegen, was du eigentlich gerade vorhast...
Dino schrieb:> Mit welchem Ansatz (Script, Tool, Software-Debugger etc.)
Du fragst nach Tools, dann werden dir die üblichen Tools/vorgehensweisen
genannt, und dann fragst du, wozu man das braucht...
Du kannst einen Nagel auch mit einem Stein ins Holz treiben, mit 'nem
Hammer gehts dann aber vielleicht doch etwas besser und leichter.
Dino schrieb:> Ich verstehe noch nicht, warum ich ein Testframework brauche und der> Vorschlag von MaWin nicht alleine ausreicht? Was sind die Vorteile> davon?
Bei Unit Tests hat man eine Reihe unabhängiger Testcases, die alle in
unbekannter Reihenfolge ausgeführt werden. Am Ende sieht man bei den
Testframeworks von jedem Test ob er durchlief, und welche Annahmen
fehlgesclagen sind. MaWins variante wird beim ersten Testcase bei einem
Fehler beendet, und man sieht nichtmehr von jedem Testcase ob er noch
geht. Zusäzlich können einige Tools die code coverage anzeigen, also
welche Codezeilen getestet sind und welche nicht. Natürlich können Tests
nicht alle Fehler aufdecken, z.B. kann es passieren, das man eine
Situation, die man vergist zu implementieren, davir auch vergessen hat,
zu testen. Oder dass ein Fehler nur unter bestimmten Laufzeitbedingungen
auftritt, und deshalb in den Tests nicht auffallen. Alles schon erlebt.
Es ist generell eine gute Idee die requirements als erstes in test cases
zu giessen und dann erst mit der Implementierung der eigentlichen
Funktion zu beginnen. Dass erlaubt dann in der Edit/compile loop sehr
schnelles Feedback wenn man dabei gleich die Tests ausführt.
Scheue mich vor einem Testframework, die vielen Files (GMock etc.) sehen
für mich nach Frickel-Arbeit aus, bis alles läuft.
Gibt es eine fertige Lösung (Eclipse etc.), wo ich die C-Sourcen
hinzufüge, kompilieren kann und ein Testframework an Board habe?
Daniel A. schrieb:> Bei Unit Tests hat man eine Reihe unabhängiger Testcases, die alle in> unbekannter Reihenfolge ausgeführt werden.
Ich vermute ja mal, dass es sich nur um eine unglückliche Formulierung
handelt, aber unerwähnt lassen sollten wir das dennoch nicht: Ein Test,
bei dem ein relevanter Parameter unbekannt ist, ist sicher kein sehr
guter Test. Die Reihenfolge der Testfälle muss nur variieren, wenn ich
befürchten muss, dass sie einen Einfluss auf das Testergebnis hat.
(Wobei es sicher nicht schadet, das grundsätzlich erst einmal anzunehmen
bzw. das auch zu testen selbst wenn ich glaube, dass sie keinen Einfluss
hat.) Wenn nun der Test aber einen Fehler erkennt, der nur bei einer
bestimmten Reihenfolge auftritt, die ich aber nicht kenne, ich den
Fehler also nicht reproduzieren kann, dann wird mir das nur wenig
helfen, den Fehler zu finden und zu beseitigen. Sagen wir also lieber,
die Testcases werden in verschiedenen, idealerweise in allen mögliche
Reihenfolgen abgearbeitet (wobei der Aufwand dann natürlich exponentiell
mit der Anzahl der Testfälle wächst).
A. H. schrieb:> Daniel A. schrieb:>> Bei Unit Tests hat man eine Reihe unabhängiger Testcases, die alle in>> unbekannter Reihenfolge ausgeführt werden.>> Ich vermute ja mal, dass es sich nur um eine unglückliche Formulierung> handelt, aber unerwähnt lassen sollten wir das dennoch nicht: Ein Test,> bei dem ein relevanter Parameter unbekannt ist, ist sicher kein sehr> guter Test.
Nein, ich habe dies Absichtlich so formuliert. Jeder Testcase muss
voneinander vollständig unabhängig sein. Die Reihenfolge darf niemals
ein relevanter Parameter werden, sonnst ist entweder der Testcase
unvollständig, oder das Programm hat einen Designfehler und das
Programmdesign muss angepasst werden.
A. H. schrieb:> Sagen wir also lieber,> die Testcases werden in verschiedenen, idealerweise in allen mögliche> Reihenfolgen abgearbeitet (wobei der Aufwand dann natürlich exponentiell> mit der Anzahl der Testfälle wächst).
Vermutlich ist mir deshalb kein Testframework bekannt, welches dies tut.
Die Tests würden zu lange dauern.
Daniel A. schrieb:>...> Vermutlich ist mir deshalb kein Testframework bekannt, welches dies tut.
Junit hat das lange Zeit erlaubt, aber generell sollen unit tests
tatsächlich völlig unabhängig lauffähig sein. Braucht man eine bestimmte
Reihenfolge gibt es nur den Weg einen Satz Tests sequentiell von einer
Testfunktion aufrufen zu lassen.
Daniel A. schrieb:
> Bei Unit Tests hat man eine Reihe unabhängiger Testcases, die alle in> unbekannter Reihenfolge ausgeführt werden.
Worüber ich hier gestolpert bin ist die Formulierung „in unbekannter
Reihenfolge“. Natürlich sind in einem guten Design die Funktionen eines
Moduls idealerweise seiteneffektfrei, insbesondere also unabhängig von
der Geschichte der Aufrufe. Dann sind auch die Tests in diesem Sinne
unabhängig, also unabhängig von ihrer Reihenfolge. Dann macht es aber
auch keinen Sinn, die Reihenfolge zum Gegenstand der Diskussion zu
machen, insbesondere zu fordern – so zumindest lese ich diese
Formulierung – dass ihre Reihenfolge unbekannt sein soll. Darüber hinaus
kann ich mir eigentlich gar keine Situation vorstellen, in der es
sinnvoll wäre zu fordern, dass eine Randbedingung eines Tests unbekannt
ist. Je mehr ich über einen Tests weiß, je besser ist es, und das gilt
meines Wissens nach immer.
Andererseits sind nun mal nicht alle Module seiteneffektfrei. Dann muss
ich die Reihenfolge zu einem Testparameter machen. Nun kann man
argumentieren, dass dann jede zu testende Reihenfolge ein eigener
Testfall ist und diese Testfälle wechselseitig wieder unabhängig sind –
einverstanden. Mein Grundproblem aber bleibt: Entweder die Reihenfolge
ist wichtig, dann muss sie bekannt sein, oder sie ist es nicht, dann
muss ich nicht darüber reden; aber selbst dann ist es immer noch besser,
sie zu kennen. Eine unbekannte Reihenfolge aber ist in keinem Fall
wünschenswert oder gar notwendig. Aber wie gesagt, ich denke, das war
nur eine etwas unglückliche Formulierung.
A. H. schrieb:> Mein Grundproblem aber bleibt: Entweder die Reihenfolge> ist wichtig, dann muss sie bekannt sein, oder sie ist es nicht, dann> muss ich nicht darüber reden; aber selbst dann ist es immer noch besser,> sie zu kennen. Eine unbekannte Reihenfolge aber ist in keinem Fall> wünschenswert oder gar notwendig. Aber wie gesagt, ich denke, das war> nur eine etwas unglückliche Formulierung.
Es gibt Frameworks, die führen Tests in "zufälliger" Reihenfolge, geben
dann aber im Fehlerfall den seed für den RNG mit aus und erlauben so die
Wiederholung in genau dieser Reihenfolge. Die Reihenfolge ist somit
zufällig und bekannt :-)
Hier ein Beispiel, bei dem man dass auch wieder abschalten kann (was zum
Schmunzeln):
http://ruby-doc.org/stdlib-1.9.3/libdoc/minitest/unit/rdoc/MiniTest/Unit/TestCase.html#method-c-i_suck_and_my_tests_are_order_dependent-21