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: GCov Jansson Summary 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): CMocka Test Results