Jag är vän av enhetstestning. Speciellt den variant som innebär att man skriver tester före kod. Den tekniken är känd som TDD – Test Driven Development. Du kan läsa alla mina postningar om TDD genom att klicka här.
Men i denna postning tänkte jag introducera en enkel idé för att komma igång med enhetstestning, utan att behöva några speciella verktyg. Det finns nämligen en hel uppsjö med enhetsverktyg för de populära språken – ofta flera konkurrerande verktyg till varje språk! För att slippa bry sig/lära sig/installera och sätta sig in i något enskilt enhetstestverktyg tänkte jag berätta hur jag gör när jag stöter på någon ”främmande miljö” jag behöver skriva enhetstester i, där jag inte orkar sätta mig in i ytterligare ett enhetstestningsverktyg.
Idén är att ASSERT() är det enda vi behöver för att göra enhetstester!
Och assert() finns i så gått som alla miljöer man stöter på – kanske med namn som assume eller något liknande. Och om det inte finns, kan man lätt skriva en egen assert-metod. T.ex. mha exceptions- exit() eller halt-instruktioner, beroende på vilket programmeringsspråk/scriptspråk man nu befinner sig i.
Jag håller mig till programmeringsspråket C i denna postning, då jag tror att det är det mest spridda språket och därför är lätt för många att ”översätta” till sitt favoritspråk.
Låt säga att vi vill utveckla en metod som kontrollerar om en textsträng innehåller en annan textsträng. Vi kallar metoden ”Contains()” och den har följande signatur:
int Contains(char* source, char* substring);
Vi vill alltså att Contains(”abc”, ”ab”) ska bli ”-1” medan Contains(”abc”, ”g”) ska bli ”0”.
Här är skelettet till ett enkelt C-program:
#include <assert.h>
int main() {
MainLoop();
}
int MainLoop() {
}
Jag har lagt till ”include <assert.h>” för att få med assert()-funktionen i programmet. Nu lägger vi till en metod ”RunSelfTests()” som är till för att köra de automatiska enhetstesterna när programmet körs:
#include <assert.h>
int main() {
RunSelfTests();
// Alla självtester gick igenom, kör huvudprogrammet!
MainLoop();
}
int MainLoop() {
}
int RunSelfTests() {
}
// Kompileringsrad:
// gcc -o test1 test1.c
Ovanstående kodskelett är allt vi behöver för att komma igång med enhetstestning i C!
Nu kommer vi till själva kärnan i detta inlägg: enhetstesterna.
Ett enhetstest testar en ”punkt” i beteendet hos en ”enhet”/”aspekt” av programvaran. Rent konkret kontrollerar man att resultatet (utdata) av viss speciell indata är korrekt.
Man väljer gärna enkla fall, så att enhetstestet blir läsbart, lätt att skriva och förstå.
I vårat fall listar vi helt enkelt upp några specialfall och det förväntade resultatet av våran funktion Contains():
- Contains(”abc”, ”abc”) ska vara sant
- Contains(”abc”, ”def”) ska vara falskt
- Contains(”olof”, ”of”) ska vara sant
.. och så vidare tills vi känner att vi kan ”lita” på våran funktion Contains() till 90% (100% är dumt att sikta på! Buggar finns alltid kvar i alla program, det lär man sig som programmerare med åren..)
#include <assert.h>
int Contains(char* source, char* substring) { return 0; }
int main() {
// Kör själv-tester (asserts) först av allt.
RunSelfTests();
// Alla självtester gick igenom, kör huvudprogrammet!
MainLoop();
}
int MainLoop() {
// ej implementerad ännu..
}
int RunSelfTests() {
assert(Contains("abc", "abc"));
assert(Contains("abc", "bc"));
assert(Contains("olof", "of"));
assert(Contains("abc", ""));
assert(!Contains("bc", "abc"));
}
Vi kan testa att kompilera & köra vårat program med ”gcc -o test1 test1.c” eller om du kör Visual Studio i Windows någon F-knapp. När vi kör programmet får vi ett felmeddelandet i stil med ”Assertion failed ‘Contains(”abc”, ”abc”) at row XX”.
Vi implementerar nu ”Contains” med hjälp av string.h-funktionen ”strstr” som fungerar ungefär som Contains faktiskt, men inte riktigt:
#include <assert.h>
#include <string.h>
int Contains(char* source, char* substring) {
char* pos = strstr(source, substring);
if(pos == 0)
return 0;
else
return -1;
}
int main() {
// Kör själv-tester (asserts) först av allt.
RunSelfTests();
// Alla självtester gick igenom, kör huvudprogrammet!
MainLoop();
}
int MainLoop() {
// ej implementerad ännu..
}
int RunSelfTests() {
assert(Contains("abc", "abc"));
assert(Contains("abc", "bc"));
assert(Contains("olof", "of"));
assert(Contains("abc", ""));
assert(!Contains("bc", "abc"));
}
Nu ska våran kod kompilera och köra utan att någon assert() kraschar!
Taggar: programmering, enhetstestning, tdd
Gilla detta:
Gilla Laddar in …