Task4 - adamk90/training-project-lab GitHub Wiki
Függőség leválasztás Mock keretrendszer segítségével
Új project - Jansson
Mivel a Klee használatához (Task5) egy C projektre volt szükségem, ezért úgy gondoltam, hogy már a Mock résznél megejtem a váltást. A választás egy Jansson nevű projektre esett, ami lényegében egy json kezelő könyvtár C nyelven. Ami az előző projekthez képest nagy előrelépés, hogy van hozzá dokumentáció és tutorial is a használatához.
Telepítés
A telepítés a dokumentáció alapján egyszerű volt, viszont, amiről nem ír, hogy beépítve támogatja a kódlefedettség ellenőrzést. Ehhez szükséges, hogy telepítve legyen a gcov / lcov páros és a gcc. A makefile-okat a következő módon kell legenerálni:
cmake -DJANSSON_COVERAGE=ON -DJANSSON_COVERALLS=ON -DCMAKE_BUILD_TYPE=Debug
Majd ezek után a leírt módon folytatni a telepítést:
make
és még a kódlefedettség vizsgálatot is el kell végezni:
make coverage
Ekkor létrejön egy coverage nevű könyvtár a build mappában, amibe bele van generálva az lcov kimenetének a html változata. Ezt megnyitva az alábbi eredményt kaptam:
Látszik, hogy jó a lefedettség, a függvények szinte 100%-os lefedettséggel rendelkeznek.
Unit-test framework: CMocka
A Mock-oláshoz a CMocka unit-test keretrendszert használtam, ami szintén egy viszonylag jól dokumentált projekt.
Teszt kiválasztása
C-ben nincsenek objektumok, ezért nem objektumokat kell mock-olni, hanem függvényeket. Mivel nem sok külső hívás történik, ezért a Jansson tutorialjában lévő kódot mock-olom. A tutorialban egy olyan egyszerű kis program szerepel, ami a curl könyvtár és a GitHub API segítségével egy megadott repository-ról lekérdezi a commit-okat és kiírja őket a standard output-ra.
A Mock-olás
A tutorial forrásfájljait módosítottam, hogy megfeleljen a célnak. Egyrészt a main függvényt kivettem és annak tartalmát két részre osztottam: egy get_first és egy get_result függvényre. A get_result felel azért, hogy a megadott felhasználónév és repository kombinációhoz visszaadja a request által lekért json-t. A get_first pedig azért, hogy a visszaadott commit-ok közül kinyerje az elsőből az sha-t és a commit szövegét. Másrészt készítettem header-t hozzá, hogy a teszt forráskódjába be lehessen hívni. A teszthez is tartozik header és el van benne készítve a wrapper függvény (__wrap előtag szimbólummal). Ezt egyébként akkor is lehet használni, ha még a függvény nincs definiálva. A forrásfájlók megtalálhatóak itt.
A wrap függvény:
char * __wrap_get_result(const char *user, const char *repository) {
check_expected_ptr(user);
check_expected_ptr(repository);
return mock_ptr_type(char *);
}
Ez az a függvény, ami a request-re hív (mock-olhattam volna a request-et is egyébként), a wrapper viszont annyit csinál, hogy ellenőrzi a kapott paramétereket és visszatér egy később megadott karakterlánccal.
Egy tesztfüggvény:
static void test_successful_get_first(void **state) {
(void) state;
expect_string(__wrap_get_result, user, "AdamK90");
expect_string(__wrap_get_result, repository, "training-project-lab");
const char *cjson = "[{\"sha\": \"2a779ce866d8207a4706e9c54d3e0032f6ee2821\",
\"commit\":{\"message\":\"Add files via upload\"}}]";
char *json = (char*)malloc(strlen(cjson) + 1);
strcpy(json, cjson);
will_return(__wrap_get_result, json);
int error;
commit_data d = get_first("AdamK90", "training-project-lab", &error);
assert_string_equal(d.sha, "2a779ce866d8207a4706e9c54d3e0032f6ee2821");
assert_string_equal(d.message, "Add files via upload");
assert_int_equal(error, 0);
}
A függvény fejléce adottság, hogy a cmocka kezelni tudja. Az expect_string hívások ellenőrzik, hogy majd a get_result-ot jó paraméterekkel hívja-e meg a tesztelés során az a függvény, amit tesztelünk (jelen esetben a get_first van tesztelés alatt és a get_result a mock-olt). A will_return pedig megmondja, hogy mivel fog visszatérni a wrapper függvény (valójában nem a visszatérést mondja meg, ha lenne több mock() hívás a wrapperben, akkor sorban azoknak a visszatérését mondaná meg). Ide beteszek egy valid json-t és meghívom a get_first-öt a megfelelő adatokkal. Az assert-ek pedig szokás szerint ellenőrzést végeznek (az sha-nak meg kell egyeznie a json-ben megadott sha-val... error pedig 0 lesz, mivel nincs hiba).
A fordításnál használni kell a gcc linkerének wrap szolgáltatását, ami azt tudja, hogy egy megadott szimbólum, ha nem definiált, akkor keres hozzá egy __wrap_szimbólum definíciót és mindenhol helyettesíti azzal.
Ez nálam nem tökéletesen működött, mert valamiért, ha a curl-ös hívást használja a get_result, akkor nem wrappelte be a fordító, ezért ki kellett kommentelnem a github_commits_mod.c-ben a definíciót és úgy működött. A fordítás a forrásokra valahogy így nézett ki:
gcc test_github_commits.c github_commits_mod.c -Wl,--wrap=get_result -lcmocka -ljansson -lcurl -o test
A futtatás eredménye pedig (a fent részletezett mellett további 2 teszt van a teszt forrásban):