it-swarm.dev

Gute Unit-Test-Beispiele für Embedded C-Entwickler

Ich werde nächste Woche mit meiner Abteilung über Unit-Tests und testgetriebene Entwicklung sprechen. Als Teil davon werde ich einige Beispiele aus der Praxis aus einem Code zeigen, den ich kürzlich geschrieben habe, aber ich möchte auch einige sehr einfache Beispiele zeigen, die ich im Vortrag schreiben werde.

Ich habe im Internet nach guten Beispielen gesucht, aber ich habe mich bemüht, solche zu finden, die für unseren Entwicklungsbereich besonders geeignet sind. Fast die gesamte Software, die wir schreiben, sind tief eingebettete Steuerungssysteme, die auf kleinen Mikrocontrollern ausgeführt werden. Es gibt eine Menge C-Code, der sich leicht auf Unit-Tests anwenden lässt (ich werde über Unit-Tests auf dem PC und nicht auf dem Ziel selbst sprechen), solange Sie sich von der untersten Ebene fernhalten: dem Material, das direkt spricht zu den Mikrocontroller-Peripheriegeräten. Die meisten Beispiele, die ich gefunden habe, basieren jedoch eher auf der Verarbeitung von Zeichenfolgen (z. B. das hervorragende Dive Into Python), und da wir kaum Zeichenfolgen verwenden, ist dies nicht wirklich geeignet ( Zu den einzigen Bibliotheksfunktionen, die unser Code normalerweise verwendet, gehören memcpy, memcmp und memset, sodass etwas, das auf strcat oder regulären Ausdrücken basiert, nicht ganz richtig ist. .

Also zur Frage: Kann jemand einige gute Beispiele für Funktionen anbieten, mit denen ich Unit-Tests in einer Live-Sitzung demonstrieren kann? Eine gute Antwort wäre meiner Meinung nach (vorbehaltlich Änderungen) wahrscheinlich:

  • Eine Funktion, die so einfach ist, dass jeder (auch diejenigen, die nur gelegentlich Code schreiben) sie verstehen kann.
  • Eine Funktion, die nicht sinnlos erscheint (d. H. Die Parität oder CRC zu berechnen, ist wahrscheinlich besser als eine Funktion, die zwei Zahlen miteinander multipliziert und eine Zufallskonstante addiert);
  • Eine Funktion, die kurz genug ist, um vor einem Raum von Menschen zu schreiben (ich kann die vielen Zwischenablagen von Vim nutzen, um Fehler zu reduzieren ...);
  • Eine Funktion, die Zahlen, Arrays, Zeiger oder Strukturen als Parameter verwendet und etwas Ähnliches zurückgibt, anstatt Zeichenfolgen zu verarbeiten.
  • Eine Funktion mit einem einfachen Fehler (z. B. > eher, als >=) Das ist einfach einzufügen, würde in den meisten Fällen immer noch funktionieren, würde aber mit einem bestimmten Edge-Fall brechen: einfach zu identifizieren und mit einem Unit-Test zu beheben.

Irgendwelche Gedanken?

Obwohl es wahrscheinlich nicht relevant ist, werden die Tests selbst wahrscheinlich in C++ mit dem Google Test Framework geschrieben: Alle unsere Header haben bereits das #ifdef __cplusplus extern "C" { umhüllen sie; Dies hat bei den Tests, die ich bisher durchgeführt habe, gut funktioniert.

21
DrAl

Hier ist eine einfache Funktion, die eine Prüfsumme über len Bytes generieren soll.

int checksum(void *p, int len)
{
    int accum = 0;
    unsigned char* pp = (unsigned char*)p;
    int i;
    for (i = 0; i <= len; i++)
    {
        accum += *pp++;
    }
    return accum;
}

Es hat einen Zaunpfostenfehler: In der for-Anweisung sollte der Test i < len Sein.

Was Spaß macht, ist, wenn Sie es auf eine Textzeichenfolge wie diese anwenden ...

char *myString = "foo";
int testval = checksum(myString, strlen(myString));

sie erhalten die "richtige Antwort"! Dies liegt daran, dass das zusätzliche Byte, das mit einer Prüfsumme versehen wurde, der Nullzeichenfolgenabschluss war. Sie können diese Prüfsummenfunktion also in Code einfügen und möglicherweise sogar damit versenden, ohne ein Problem zu bemerken - das heißt, bis Sie sie auf etwas anderes als Textzeichenfolgen anwenden.

Hier ist ein einfacher Unit-Test, der diesen Fehler anzeigt (meistens ... :-)

void main()
{
    // Seed the random number generator
    srand(time(NULL));

    // Fill an array with junk bytes
    char buf[1024];
    int i;
    for (i = 0; i < 1024; i++)
    {
        buf[i] = (char)Rand();
    }

    // Put the numbers 0-9 in the first ten bytes
    for (i = 0; i <= 9; i++)
    {
        buf[i] = i;
    }

    // Now, the unit test. The sum of 0 to 9 should
    // be 45. But if buf[10] isn't 0 - which it won't be,
    // 255/256 of the time - this will fail.
    int testval = checksum(buf, 10);
    if (testval == 45)
    {
        printf("Passed!\n");
    }
    else
    {
        printf("Failed! Expected 45, got %d\n", testval);
    }
}
15
Bob Murphy

Was ist mit der Implementierung einer Sortierfunktion wie Blasensortierung ? Sobald die Sortierfunktion funktioniert, können Sie mit binäre Suche fortfahren, was ebenso gut für die Einführung von Unit-Tests und TDD geeignet ist.

Das Sortieren und Suchen hängt von Vergleichen ab, die leicht falsch sind. Dazu gehört auch das Vertauschen von Zeigern, um die vorsichtig vorgegangen werden muss. Beide sind fehleranfällig, also zögern Sie nicht, es zu vermasseln :)

Noch ein paar Ideen:

  • Unit-Tests helfen beim Refactoring sehr. Sobald Ihre Blasensortierung funktioniert, können Sie sie in eine leistungsstärkere Sortierung wie qsort ändern. Die Tests sollten dennoch bestanden werden, um zu beweisen, dass auch Ihre neue Sortierfunktion funktioniert.
  • Das Sortieren ist einfach zu testen, das Ergebnis ist entweder sortiert oder nicht, was es zu einem guten Kandidaten macht.
  • Das gleiche gilt für die Suche; es existiert entweder oder es existiert nicht.
  • Das Schreiben von Tests zum Sortieren eröffnet Diskussionen darüber, welche Art von Eingabe für den Test verwendet werden soll (Nullelemente, zufällige Eingabe, doppelte Einträge, große Arrays usw.).
2
Martin Wickman