056.1 Lecke 1
Tanúsítvány: |
Open Source Essentials |
---|---|
Verzió: |
1.0 |
Témakör: |
056 Kollaboráció és kommunikáció |
Fejezet: |
056.1 Fejlesztői eszközök |
Lecke: |
1/1 |
Bevezetés
Több ezer szoftverfejlesztő eszköz (tool) létezik, nyílt és zárt forráskódú egyaránt. És miért is ne? A programozók szeretnek eszközöket fejleszteni maguknak és kollégáiknak; természetes, hogy sok időt fordítanak arra, hogy olyan toolt találjanak, amely jobban működik, kiküszöböl valamilyen bosszúságot a munkafolyamatból, vagy megkönnyíti a telepítést, beüzemelést (deployment).
Ez a lecke a fejlesztési folyamatra összpontosít, és elmagyarázza, hogy a különböző típusú fejlesztési eszközök hogyan illeszkednek ebbe a folyamatba. Kevés konkrét eszközt fogunk megnevezni, mert bármelyikkel megtörténhet, hogy mire ezt a leckét publikáljuk, elavultnak (deprecated) vagy korszerűtlennek (obsolate) lesznek megjelölve, és egy új kedvenc váltja fel őket.
A fejlesztés céljai
A programozási eszközök több céllal zsonglőrködnek, amelyek néha egymással ellentétesek. Íme néhány példa a fejlesztési célokra:
-
Robusztus, pontos programok készítése.
-
Gyorsan futó programok készítése.
-
Jól skálázható programok készítése.
-
Olyan programok készítése, amelyek jól alkalmazkodnak a különböző típusú felhasználókhoz, különböző eszközökhöz (például laptopokhoz, mobiltelefonokhoz és táblagépekhez) és különböző környezetekhez.
-
A fejlesztési folyamat felgyorsítása.
-
Az újonnan kifejlesztett funkciók vagy hibajavítások telepítésének felgyorsítása.
-
A fárasztó munka, valamint az olyan programozási hibák számának csökkentése, amelyek átcsúsznak a program tesztelési szakaszaiba.
-
Olyan régebbi (legacy) kódok és eszközök támogatása, amelyeket a szervezet nehezen tud lecserélni.
-
Együttműködés más, ismert eszközökkel.
-
A könnyű visszaállítás (roll-back) lehetővé tétele hibák vagy tervmódosítások esetén.
Ezek és más célok vezetnek az ebben a leckében leírt folyamatokhoz és az ezekből származó fejlesztési eszközökhöz.
Általános fejlesztési folyamatok
Ez a szakasz két általános, történelmi jelentőségű modellt állít szembe egymással: a vízesés modellt (waterfall model, amelyet egy korábbi leckében mutattunk be) és a continuous integration/continuous delivery (folyamatos integráció/folyamatos szállítás — CI/CD) modellt.
Vízesés modell
Bár a vízeséses modellt még mindig széles körben használják, főleg nagy szervezetek, sok fejlesztőnek kiesett a kegyeiből. Ez a modell az 1950-es évektől az 1970-es évekig volt népszerű. A modell elemei ma is széles körben megtalálhatók, például a követelmények formális meghatározása és a programok tesztelése közvetlenül a kiadásuk előtt (minőségbiztosítás (quality assurance)).
Már akkor széles körben hiteltelenné vált, amikor a “vízesés” kifejezést alkalmazták erre a modellre. A problémák közé tartozott:
-
A követelmények megváltoztatása nehéz volt, miután a fejlesztés megkezdődött.
-
A követelményeket és a terveket gyakran félreértették, mivel az egyszerű szöveg kétértelmű lehet. Ez a probléma olyan termékekhez vezetett, amelyek nem feleltek meg az elvárásoknak, és hónapokig tartott a javításuk.
-
A folyamat lassú volt. Egy új release csak évente egyszer, vagy még ritkábban valósulhatott meg.
-
A különböző modulok kölcsönhatása által okozott hibákat nehéz volt észrevenni a folyamat késői szakaszáig, ami további késésekhez vezetett.
-
A folyamat falakat emelt a szervezet különböző részei közé, ami rossz hatással volt a termék minőségére és a szervezeti csapatszellemre (espirit de corps).
A Continuous Integration/Continuous Delivery (CI/CD) alapelvei
A vízesés-modellt az 1970-es években egy sor mozgalom támadta meg, amelyek a ma leghíresebb (bár nem általánosan használt) modellhez vezettek: CI/CD. A CI/CD gyakorlatok átvételének állomásai közé tartozik a 2001-ben kiadott Agile Manifesto, a SCRUM és a DevOps. Az új modell a csapattagok közötti szoros kommunikáció, a végfelhasználó vagy ügyfél bevonása, a hibajavítások és új funkciók gyors bevezetése, valamint a minőség megőrzését célzó szigorú folyamatos tesztelés elveire épül a gyorsan változó — néha kaotikus — környezetben.
A CI/CD a lehető legtöbb lépést automatizálja a kódírástól a kész program végfelhasználóknak történő telepítéséig tartó szakaszokban. A CI/CD megköveteli az egyes lépések formális meghatározását és az emberi tevékenység (például a szoftver kézzel történő telepítése) helyettesítését egy program által lefuttatott eljárással. A CI/CD tehát része annak a mozgalomnak, amelyet a “minden, mint kód” (everything as code) és az “infrastruktúra, mint kód” (infrastructure as code) kifejezésként ismerünk.
Továbbá, ha egy csapat egyszer már beépítette a folyamatokat a kódba, ezeket a folyamatokat ugyanúgy lehet javítani, frissíteni és rögzíteni, mint a kódot. Bármit, amit a csapat ír, legyen szó programokról, automatizált eljárásokról vagy dokumentációról, egy verziókezelő rendszerben kell tárolni, amelyet egy következő leckében ismertetünk.
Ha több eljárást automatizálunk, azok egymás után is lefuthatnak. Ezért a csapatok gyakran beszélnek CI/CD pipeline-ról, ami azt jelenti, hogy a pipeline egy sikeres lépése automatikusan elindít egy vagy több következő folyamatot. A pipelinet automatizált módon kell felügyelni, hogy a folyamat során felmerülő bármilyen hiba esetén leálljon és értesítse a csapattagokat a hibáról.
Bár a következő szakaszokban külön-külön tárgyaljuk a CI-t és a CD-t, ezek általában összekapcsolódnak, és közöttük lévő határvonal elmosódik.
Continuous Integration (CI)
A continuous integration (folyamatos integráció) a kis változtatások gyors beépítését jelenti a kódba. A végfelhasználóknak szánt termék létrehozásához használt központi tárolóhelyet core repository (vagy core repo) néven ismerjük. Minden programozó létrehoz egy személyes munkaterületet (gyakran sandbox (homokozó) néven ismert) a számítógépén, és a core repositoryból húzza le a javítandó vagy frissítendő fájlokat.
A programozó használhat egy személyes laptopot vagy asztali számítógépet (úgynevezett lokális fejlesztőrendszert (local development system)), letöltheti a core repository megfelelő részeit, majd a helyi tesztelés után feltöltheti a változtatásokat. Alternatív megoldásként a programozó kihasználhatja a “felhőalapú számítástechnikának” nevezett népszerű irányzatot, és dolgozhat egy, a szervezete által üzemeltetett, megosztott rendszeren, a távoli fejlesztőrendszeren (remote development system).
A programozó általában egy debugger segítségével ellenőrzi a hibákat, amely egy külön program, ami ellenőrzött környezetben futtatja a programozó munkáját. Később megnézzük a hibakereső programokat és a hibák felderítésére szolgáló egyéb eszközöket.
Annak érdekében, hogy a hibákat a lehető legkorábban kiszűrje a fejlesztési folyamat során, a programozó teszteket is futtat a kódon, mielőtt feltöltené azt a core repositoryba. Ezeket unit teszteknek nevezik, mert mindegyik a kód egy-egy apró elemére összpontosít: például, hogy egy függvény megfelelően inkrementált-e egy számlálót?
A CI utolsó fázisa a programozó változtatásainak ellenőrzése a core repositoryba. Ebben a szakaszban az eszközök integrációs teszteket futtatnak, hogy megbizonyosodjanak arról, hogy a programozó nem tört meg semmit a projekt egészében.
Bármilyen messzire is jutott az automatizálás, a csapat valamelyik szakértő tagjának az integrációnál közbe kell avatkoznia, hogy megbizonyosodjon arról, hogy ezt a változtatást szeretné a csapat. Az automatizált tesztek megállapíthatják, hogy semmi sem romlott el, sőt, azt is, hogy a változás a kívánt hatást hozza-e létre a programban. Ezek a tesztek azonban nem tudják ellenőrizni a csapat által fontosnak tartott összes alapelvet.
Ezért, mielőtt a csapattagok megkezdenék a telepítést, ellenőrizniük kell a biztonságot, a kódolási szabványok betartását, a megfelelő dokumentációt és egyéb elveket. (Nem meglepő, hogy ezek közül néhány feladatra is léteznek eszközök; a lecke későbbi részében megnézzük őket.)
A CI során rengeteg tesztelésre kerül sor, és ennek gyorsan és megbízhatóan kell történnie, hogy támogassa a modern fejlesztés ütemét. Ezért a kód integrálására és a tesztek futtatására szolgáló modern eszközök automatizáltak. A programozónak képesnek kell lennie arra, hogy egyetlen parancs vagy gombnyomás segítségével futtasson egy egész unit teszt-csomagot.
Általában egy részleges vagy teljes integrációs teszt mindig lefut, amikor a programozó feltölti a kódot a core repositoryba. A tesztek által észlelt hibákat gyorsan jelenteni lehet a programozónak.
Continuous Delivery (CD)
Amint az előző szakaszban kifejtettük, a CI olyan automatizált eljárásokból áll, amelyek a program új verziójához vezetnek a core mappában. A continuous delivery (folyamatos szállítás) arra utal, ami ezt a szakaszt követően történik, hogy az új verzió kikerüljön a terepre (field), ahol a végfelhasználók használhatják. A CD-ben a D-t néha a “development” (fejlesztés) vagy a “telepítés” (deployment) mellett a “szállítás” (delivery) szóra is kiterjesztik.
A CD fő feladata a kód alapos tesztelése és feltöltése a felhasználók számítógépére vagy egy alkalmazás repositoryba.
A CD-fázisban egy sor tesztet végeznek el, hogy maximálisan biztosítsák a termék megfelelő működését. Az integrációs tesztek nagyobb csoportját lehet lefuttatni, valamint számos más tesztet is, azért, hogy meghatározzák a felhasználókra, a teljesítményre és a biztonságra gyakorolt hatásokat. A lecke egy későbbi szakaszában ismertetünk néhány ilyen tesztet.
A tesztelést a felhasználókat kiszolgáló éles rendszerektől eltérő rendszereken kell végezni. Egy katasztrofális hiba nem csak a production rendszert teheti tönkre, hanem a felhasználói adatokat is. Ezen túlmenően a tesztek versenyeznek a rendszeridőért, és rontják a felhasználóknak jutó teljesítményt.
A teszteléshez tehát a legjobb, ha egy olyan teljes számítógépes környezetet hozunk létre, amely tükrözi az éles környezetet, hasonló hardverrel és szoftverrel. A köztes környezetet, ahol a tesztelés a telepítés előtt fut, gyakran nevezik staging környezetnek.
Természetesen nem lenne praktikus, ha a tesztkörnyezet ugyanolyan méretű lenne, mint az éles, de annak alapvető elemeit (például az adatbázisokat) tartalmaznia kell.
A CD-automatizálás egyik követelménye a teszt- és az éles környezet megkülönböztetése. Minden kódtelepítést és folyamatot a célkörnyezethez kell igazítani.
A CD akkor kínálja a legtöbb lehetőséget a gyors fejlesztésre, ha a kód a szervezet saját rendszerein futó szolgáltatás. Ha például egy kiskereskedelmi egység létrehoz egy interaktív weboldalt, akkor hozzáférhet az összes webszerverhez. Ha akarjuk, naponta többször is frissíthetjük őket, és gyorsan visszavonhatjuk a változtatásokat, ha kiderül, hogy azok problémákat okoznak a felhasználók számára.
Általános szoftverfejlesztési eszközök
Ezek a szakaszok három szinten mutatják be a programozói csapatok által használt általános eszköztípusokat: kódgenerálás, tesztelés és telepítés.
Fordítók
Az alapvető programozási eszköz a compiler (fordító), amely a nagyon kifinomult, magas szintű programozási nyelveket a számítógép processzorán futó utasításokká alakítja.
A számítógépek gépi kódot futtatnak, amely bitek (egyesek és nullák) sorozataiból áll, amelyeket a processzor utasításokká és adatokká alakít: egy érték betöltése a memóriából egy regiszterbe, két regiszter értékének összeadása stb. A processzorokat a különböző formátumok és utasításkészletek különböztetik meg, így a gépi kódjuk is különböző. A gyártók megpróbálnak olyan új processzorokat kitalálni, amelyek ugyanazt a gépi kódot használják, hogy a vásárlók régi programjaikat az új processzorokon is futtathassák. Amikor a gyártók ezt teszik, akkor beszélhetünk processzorcsaládokról.
A programozás korai éveinek következő állomása az assembly nyelv volt, amely ember által olvasható kifejezésekkel, például ADD-dal (hozzáad) látta el az egyes utasításokat. A programozók assembly nyelven kódoltak, és kódjukat egy assembler nevű eszköznek adták át, amely az assembly nyelvet gépi kódra fordította. A gépi kódot gépi nyelvnek is nevezik.
Ezután jöttek létre a magasabb szintű nyelvek. Manapság összetett vezérlőfolyamatokat hozhatunk létre anélkül, hogy azok részleteit specifikálnánk; csak a kívánt eredményeket kell megadnunk. Ezekhez a programokhoz fordítóprogramra van szükség, amely a forráskódot gépi kóddá alakítja. A fordítók egészen intelligens átalakításokat és optimalizálásokat tudnak végezni.
Sok nyelv több fordítóprogrammal is rendelkezik. A nagyon népszerű nyelvekhez, mint például a C és a Java, többféle fordítóprogramot is találhatunk. A szabad és nyílt forráskódú közösségben a legtöbben a GNU Compiler Collection (GCC) vagy az LLVM fordítót használják C és C++ nyelvekhez.
Eredetileg a fordító minden egyes forráskódfájlt lefordított, hogy egy köztes objektumfájlt hozzon létre, majd az összes objektumfájlt egy programba egyesítette egy másik eszköz, a linker meghívásával. A modern fordítók egyszerre több fájlt is lefordíthatnak, hogy a fájlok határait átlépő optimalizálásokat hajtsanak végre.
A fordítók korábban assembly nyelvre fordítottak, ami azért lehetett hasznos, mert annak ellenére, hogy minden egyes processzortípus más-más gépi kódot támogatott, támogathatta ugyanazt az assembly nyelvet. Az assembly nyelveknek több változata létezik. A fordítás és a linkelés utolsó lépése a gépi kód előállítása volt. A linkeléshez hasonlóan a modern fordítóprogramok tudják, hogyan kell összerakni a kódot gépi kóddá.
Sok modern nyelvben azonban van egy másik szakasz is: a köztes kód, amelyet általában bájtkódnak neveznek. Ezt a kódot olyan bináris formátumba fordították, amely elég magas szintű ahhoz, hogy hordozható legyen. A Java nyelvet például bájtkóddá fordítják, hogy a program számos különböző típusú processzorra betölthető legyen.
A forráskódból történő bájtkód előállítása során a fordító elvégezte a munka nagy részét. Ezután minden számítógépen megtalálható a program saját verziója, az úgynevezett virtuális gép (virtual machine), amely a bájtkódból a virtuális gép által végrehajtható utasításkészletté alakítja a programot. Néha a bájtkódot is gépi kóddá fordítják. A végfelhasználók számára kényelmesebb a bájtkódból az utasításkészletbe való átmenet, mint a magas szintű programozási nyelvből a gépi kódba való átmenet. Amikor bevezették, ez volt a Java nagy előnye, mert a tervezői azt akarták, hogy a Java egy virtuális gépi plug-inben fusson a webböngészőkben, ahol a felhasználó processzora és operációs környezete nagyon változatos lehet.
A Java tervezői a bájtkód előnyeit a “Compile once, run anywhere.” (fordítsa egyszer, futtassa bárhol) marketingmondattal hirdették. A bájtkód néhány további előnye később jelent meg. Új nyelveket lehetett létrehozni, amelyek egészen más élményt nyújtottak a programozóknak (remélhetőleg megkönnyítve a programozási munkát és karbantarthatóbb kódot produkálva), miközben ugyanazt a bájtkódot hozták létre, amelyet a Java virtuális gépek is támogattak. E nyelvek függvényei könnyen keverhetők voltak a meglévő Java programokkal.
Végezetül pedig vannak olyan nyelvek, mint például a Python, amelyeknél egyáltalán nem kell forráskódot fordítanunk: egyszerűen csak beírjuk az utasításokat egy interpreter (értelmező) nevű feldolgozó eszközbe, amely a kódot közvetlenül gépi kóddá alakítja és végrehajtja. Az értelmezők lassabbak, mint a fordítóprogramok, de egyesek elég hatékonyak ahhoz, hogy ne jelentsenek nagy teljesítménybeli hátrányt. Mégis, a Python nyelv néhány népszerű könyvtára olyan függvényeket tartalmaz, amelyek a fejlesztők számára az értelmezett nyelven elérhetőek, de azért, hogy felgyorsítsák a végrehajtást, C nyelven lettek leprogramozva.
Sok értelmezett nyelv is biztosít fordítóprogramokat. Ezek vagy bájtkódot, vagy gépi kódot állítanak elő, amely aztán gyorsabban fut, mint az interpreter.
A programozóknak a fájlok és funkciók kezelését segítő build eszközök állnak rendelkezésre. Egy összetett program több száz fájlt tartalmazhat, és a programozónak különböző időpontokban (például a hibakeresés támogatása érdekében) különböző fájlkombinációkat kell lefordítania különböző opciókkal. Egy build eszköz lehetővé teszi a programozók számára, hogy különböző opciókat és fájlkombinációkat tároljanak, és könnyen kiválaszthassák a kívánt build típusát. A Java és a rokon nyelvei esetén a Maven és a Gradle gyakori build eszközök.
Kódgeneráló eszközök
A programozónak nem kell üres képernyővel kezdenie a kódolást. Nemrégiben olyan szolgáltatások jelentek meg, amelyek kódot generálnak a kívánt program egyszerű szöveges leírása alapján. Ez a generatív mesterséges intelligencia egy formája, és más ilyen szolgáltatásokhoz hasonlóan egyesek panaszkodnak, hogy korábbi programozók munkáját használja fel, és (eddig) kevésbé robusztus forráskódot állít elő. Az etikai vitáktól eltekintve sok programozó szerint az automatikus kódgenerálás jelentősen javította a termelékenységüket.
A kódgenerálás egyik formája, amely sok programozási nyelvben már évek óta elérhető, a refactoring. Ez egy nagy programot vizsgál meg, amely idővel könnyen fájlok és függvények kusza összevisszaságává alakulhat. A refaktorálás a függvényeket áthelyezi, hogy a program logikusabb struktúrát kapjon a jobb karbantarthatóság és a forráskód duplikációjának csökkentése érdekében.
Egyes programozók feladata más kódok működésének reprodukálása. Előfordulhat, hogy új programot kell írniuk egy olyan régebbi program helyett, amelynek hiányzó forráskódja visszavonásra szorul; vagy egy versenytárs programját kell utánozniuk. Az ilyen jellegű kutatást reverse engineering-nek nevezik. Az egyik hasznos eszköz erre a célra a disassembler, amely a gépi kódot assembly nyelvvé alakítja. Bájtkódhoz is léteznek disassemblerek.
Debuggerek
A legtöbb programozó több időt tölt hibakereséssel (debugging), mint kódolással. Az emberek egyszerűen nem gondolkodnak tökéletesen logikusan, ezért elfelejtenek néhány olyan részletet, amelyet a számítógép megkövetel ahhoz, hogy a program úgy fusson, ahogyan szeretnénk. Valószínűleg a kódunk már az első próbálkozásnál elbukik, egy debugger pedig nagy segítség lehet a hiba feltárásához.
A hibakereső segíti az intenzív kutatómunkát, így órákkal csökkenthetjük a hibák megtalálásának folyamatát.
A programozó megkérheti a programot, hogy a program egy kulcsfontosságú helyen (breakpoint (megszakítási pont)) álljon meg, például egy függvény vagy ciklus elején. A hibakereső képes megjeleníteni a változók és akár a számítógép regisztereinek értékeit a program aktuális pontján. A programozó az egyes utasításokat egyenként is lefuttathatja, és láthatja az eredményeket (single stepping). Ha a programozó látni akarja, hogy egy változó mikor és hogyan változik a futás során, akkor beállíthat egy watchpoint-ot.
A szabad és nyílt forráskódú világban a legjelentősebb debugger (különösen a C és C++ nyelvek esetében), a GNU debugger, amely a korábban említett GNU fordítóval működik. Más nyelveknek is van dedikált debuggerük.
Analitikai eszközök
Bár a hibakeresés általában viszonylag gyorsan feltárja a hibákat, jobb, ha az elírásokat és más alapvető problémákat már a programozási folyamat elején ki tudjuk küszöbölni. A program ellenőrzéséhez számos ötletes elemző eszköz létezik. A statikus elemzés (static analysis) a program kódját vizsgálja. A dinamikus elemzés (dynamic analysis) lefuttat egy programot és a végrehajtás során ellenőrzi a problémákat.
A statikus elemzés egyik legkorábbi formáját linternek nevezték. Ez képes felismerni például, ha egy lebegőpontos változó értékét egy egész szám (integer) típushoz rendeljük. Ez vagy problémát okoz, vagy nem, valamint a fordítóprogram is vagy észreveszi, vagy nem. Éles környezetben azonban hibás eredményekhez vezethet.
Manapság a legtöbb fordítóprogram képes elvégezni a linterek munkáját. Néhány fordító, mint például a Rust nyelvhez készült fordító, szigorúan elutasítja a rosszul megírt kódot.
Sok más típusú analitikai eszköz a fordítóprogramtól külön fut. A biztonsági elemző eszközök például megtalálhatják azokat a problémákat, amelyek a programot sebezhetővé teszik a hackerek számára. Gyakori fejleszőti hiba például az, amikor a kód meghív egy függvényt, és nem ellenőrzi, hogy az adott-e vissza hibát.
Integrált fejlesztőkörnyezetek (IDE-k)
Sok programozó szövegszerkesztőt használ a kód bevitelére és szerkesztésére. A szövegszerkesztők különböznek a szövegfeldolgozó programoktól, amelyek sok formázási lehetőséget (például dőlt és félkövér betűket, gömböket, számozott listákat stb.) alkalmaznak. A szövegszerkesztő formázatlan szöveget állít elő, amit a programozási nyelvek meg is követelnek.
Vannak olyan kifinomult eszközök is, amik segítik a programozókat a programok fejlesztésében. Ezek az eszközök a használt programozási nyelvre figyelnek. Ha például egy változó vagy függvény nevét kezdjük el begépelni, az eszköz felajánlja a lehetséges befejezést. Ezek az eszközök kódolás közben ellenőrizhetik a hibákat, konzisztens és tetszetős módon formázhatják a kódot, analitikai eszközöket és hibakereső programokat futtathatnak, lefordíthatják a kódot, kezelhetik a verziókezelő rendszerekbe történő bejelentkezéseket, és egyéb feladatokat is el tudnak végezni helyettünk. Ezért nevezik őket integrált fejlesztőkörnyezeteknek (integrated development environments — IDE).
Az Eclipse egy népszerű nyílt forráskódú IDE.
A szoftvertesztelés általános típusai
A tesztelés a szoftverfejlesztés fontos része. A programozók a kód fejlesztése során unit teszteket futtatnak. Más típusú tesztek általában vagy akkor futnak, amikor a programozó a kódot felrakja (visszacheckolja) a core repositoryba, vagy pedig a deployment során.
Unit tesztelés
Láttuk, hogy a programozónak nagy figyelmet kell fordítania a hibák megtalálására, mielőtt a kódot elküldi a core repositoryba való integrálásra. A hibák felderítéséhez elengedhetetlenek a unit tesztek (egységtesztek).
A tesztek írása egyszerre művészet és tudomány, és a tesztkód mennyisége meghaladhatja a programkód mennyiségét. Létezik még egy tesztvezérelt fejlesztés (test-driven development — TDD) nevű fejlesztési modell is, amelyben a programozók a teszteket a tesztelni kívánt kód megírása előtt írják meg. A TDD hívei azt állítják, hogy ez kitölti a tesztelés hiányosságait, és segít biztosítani, hogy a kód azt csinálja, amit a programozó akar.
Fontos, hogy teszteljük a program végrehajtása során elromló dolgokat, de a jól működő dolgokat is. Ha a felhasználó vagy a program egy másik része érvénytelen bemenetet küld egy függvénynek, fontos, hogy a függvény észlelje a problémát, és megfelelő hibaüzenetet küldjön.
A JUnit egy népszerű nyílt forráskódú eszköz a Java programok unit tesztjeinek futtatására.
Integrációs, regressziós és smoke tesztelés
Míg a unit tesztek az egyes funkciók egyes műveleteire összpontosítanak, a csapatnak magasabb szintű teszteket is kell futtatnia, hogy megbizonyosodjon arról, hogy a termék egésze megfelelően működik. A tesztek általában a felhasználói viselkedést imitálják. Például egy étteremkezelő alkalmazásban a tesztek ellenőrizhetik, hogy a felhasználó megkapja-e a kért terméket.
Amikor egy program módosítása megszakít egy korábban működő funkciót, a hibát regressziónak (regression), a teszteket pedig regressziós teszteknek (regression test) nevezzük.
Amikor egy csapat a terméket releasre készíti elő, a tesztelés első szakasza gyakran nagyon rövid. A csapat a program által végzett legfontosabb tevékenységeket ellenőrzi, és hiba esetén leállítja a tesztelést, így időt takarít meg. Ezt a fajta tesztelést smoke tesztnek nevezik, mert egy ilyen könnyen meghibásodó alkalmazás olyan, mint egy rossz készülék, ami felgyullad.
Egyes termékek felhasználói interakciót igényelnek; a webes és mobil alkalmazások gyakori példák erre. Ezért hoztak létre olyan eszközöket, amelyekkel a felhasználói interakciókat lehet utánozni. A teszt automatikusan lefut, és elindítja azt a kódot, amely akkor futna le, ha a felhasználó megnyomná a gombot. A Selenium például népszerű eszköz ebben a kategóriában.
Elfogadási tesztelés
A felhasználói felülettel rendelkező programoknak a tesztelés egy extra szintjére van szükségük, amely túlmutat annak bizonyításán, hogy megfelelően reagálnak bizonyos bemenetekre. A programoknak a képernyőn is jól kell kinézniük. Az acceptance testing (elfogadási teszt) a program felhasználóra gyakorolt hatását ellenőrzi. A minőségbiztosítási csapatok általában azután futtatják ezeket a teszteket, hogy az integrációs és regressziós tesztek bizonyítják, hogy a program formailag helyes és megfelel a felhasználói elvárásoknak.
Biztonsági tesztelés
Nyilvánvalóan kritikus fontosságú szempont a biztonság, és még egy apró biztonsági hiba is komoly károkat okozhat egy szervezetnek. Láttuk, hogy a programozók analitikai eszközöket futtathatnak a kód biztonságának ellenőrzésére. A minőségbiztosítási szakaszban a tesztek azt is megállapíthatják, hogy a programban vannak-e sebezhetőségek. A tesztek rosszindulatú bemenetekkel futtatják a programot, és meggyőződnek arról, hogy a program visszautasítja a bemenetet anélkül, hogy hibázna, nem kívánt műveleteket hajtana végre, vagy érzékeny információkat fedne fel.
Az egyik fajta teszt, amely bizonyos helyzetekben értékesnek bizonyult, a fuzz testing. A tesztelési keretrendszer egyszerűen véletlenszerű szemétből álló karakterláncokat generál, és azokat bemenetként elküldi a programnak. Ez időpocsékolásnak tűnhet, de gyakran olyan sebezhetőségeket tár fel, amelyeket a szokásos tesztelés nem.
Teljesítménytesztelés
Miután úgy ítélték meg más tesztek alapján, hogy a program helyesen működik, a csapatoknak meg kell határozniuk, hogy elég gyorsan fut-e. A performance testing-hez (teljesítménytesztelés) olyan környezetre van szükség, amely hasonló ahhoz, ahol a felhasználók interakcióba lépnek a programmal. Ha például a felhasználók nagy távolságból, hálózaton keresztül küldik majd a kéréseket, a teljesítménytesztelést is nagy távolságú hálózaton kell elvégezni.
Egyes programkönyvtárak tesztelése benchmarkok segítségével történik: különböző könyvtárak vagy ugyanazon könyvtár különböző verzióinak összehasonlítására használt szabványos tesztek.
Általános telepítési környezetek
Egy CI/CD eszköz kifinomult módon teszi lehetővé a pipelineok felépítését. A számítógépes programokhoz hasonlóan egy pipeline is tartalmazhat teszteket és ágakat (branch). A leágazásokkal a tevékenységek egy részét a tesztkörnyezetben, egy másik részét pedig az éles környezetben végezhetjük el. Az eszköz segítségével automatikusan telepíthetjük a különböző programokhoz szükséges megfelelő adatbázist vagy más szoftvert.
A CD átfedésben van a DevOps-szal. A felhőben a CD és a DevOps eszközök automatizált módon hozzák létre a virtuális számítógépes rendszereket a programok futtatásához szükséges összes komponenssel. Az automatizált eszközök (néha orchestration eszközöknek is nevezik őket) ellenőrzik a virtuális rendszerek meghibásodását, és automatikusan újraindítják azokat.
A CD eszköz fő feladata az egyes pipelineok elindítása és végigjárása. Az eszköz ellenőrzi a pipeline minden egyes szakaszának eredményeit, és dönt a folytatásról vagy a leállításról. Az eszköz lehetővé teszi az ütemezést is, és naplózza a tevékenységeit.
Gyakran előfordul, hogy egy feladatot kisebb változtatásokkal többször is le kell futtatnunk. A csapatok például különbséget tesznek a tesztkörnyezetbe való telepítés és az éles környezetbe való telepítés között. Ezért a CD-eszközök olyan paramétereket biztosítanak, amelyeket a pipeline futtatásakor különböző értékekkel tölthetünk ki.
Néha egy folyamat olyan parancsokat igényel, amelyeket hagyományosan a terminálon adtak meg. Ezért a CD-eszközök tetszőleges parancsok futtatásához biztosítanak mechanizmusokat. Általában olyan pontokat, mint a preprocess
a pipeline egy adott szakasza előtti parancsok futtatásához, és a postprocess
, a pipeline egy adott szakasza utáni parancsok futtatásához.
Az ebben a fejezetben ismertetett automatizáláshoz a Jenkins az egyik ilyen legismertebb nyílt forráskódú eszköz.
Gyakorló feladatok
-
Milyen módokon lehet ellenőrizni egy program biztonságosságát?
-
Miért írhatnánk több unit tesztet egyetlen függvényhez a programban?
Gondolkodtató feladatok
-
Csapatunk egy régi alkalmazást örökölt, amely lassan fut és nehezen bővíthető. Milyen módon lehetne javítani az alkalmazáson anélkül, hogy kidobnánk és újat írnánk a semmiből?
-
Nagy projekteknél gyakran előfordul, hogy az A csapat egy funkciót a B csapat által karbantartott rendszer egy részében szeretne megvalósítani, de a B csapat nem tekinti ezt a funkciót prioritásnak. Hogyan kódolhatja az A csapat a funkciót a B csapat projektjének részeként?
Összefoglalás
Ez a lecke a fejlesztés során használt eszköztípusokat ismertette: fordítók és más kódgeneráló eszközök, elemzők, tesztek, valamint az integrációt és a szállítást automatizáló CI/CD-eszközök. Mindegyik tevékenység végrehajtására számos lehetőség van, és egy ma még népszerű eszköz egy éven belül lecserélődhet. Annak megértése, hogy ezek az eszközök hogyan illeszkednek egymáshoz a fejlesztési folyamatban, segít azonosítani, hogy mire van szükségünk.
Válaszok a gyakorló feladatokra
-
Milyen módokon lehet ellenőrizni egy program biztonságosságát?
Először is, szakértők megvizsgálhatják a kódot.
Számos statikus és dinamikus elemző eszköz létezik a programozást veszélyeztető rossz programozási gyakorlatok kiszűrésére.
Más eszközök rosszindulatú bemenetet küldenek a futó programoknak, és ellenőrzik a reakciókat.
-
Miért írhatnánk több unit tesztet egyetlen függvényhez a programban?
Egy függvénynek általában sokféle bemeneti adatot kell kezelnie, és mindegyik típus megérdemli a saját tesztjét. A függvény például speciális módon kezelheti a nulla bemeneti értéket. Az érvénytelen bemenetet is előre kell látnunk, és teszteket kell írnunk, amelyek megmutatják, hogy a függvény megfelelően kezeli-e azokat.
Válaszok a gondolkodtató feladatokra
-
Csapatunk egy régi alkalmazást örökölt, amely lassan fut és nehezen bővíthető. Milyen módon lehetne javítani az alkalmazáson anélkül, hogy kidobnánk és újat írnánk a semmiből?
Először is győződjünk meg róla, hogy a projekt verziókezelt, ha korábban nem lett verziókezelő alá helyezve!
Egy új funkció hozzáadása után futtassunk regressziós teszteket, hogy megállapítsuk, hol hibázik a program, és bízzuk meg a programozókat, hogy kiderítsék, mely funkciók a felelősek! Ezeket a függvényeket szelektíven ki tudjuk cserélni.
A teljesítményteszteléssel azonosíthatók a lassan futó egyes funkciók, így az erőfeszítéseket a legkevésbé hatékony kód javítására vagy cseréjére összpontosíthatjuk.
Ha a kód olyan nyelven készült, amely már nem népszerű, fontoljuk meg, hogy a funkciók a csapat által kedvelt nyelven adjuk hozzá. Győződjünk meg arról, hogy az új nyelven megírt funkciók integrálhatók a régi funkciókhoz.
-
Nagy projekteknél gyakran előfordul, hogy az A csapat egy funkciót a B csapat által karbantartott rendszer egy részében szeretne megvalósítani, de a B csapat nem tekinti ezt a funkciót prioritásnak. Hogyan kódolhatja az A csapat a funkciót a B csapat projektjének részeként?
A B csapat engedélyezheti az A csapatnak, hogy új branchet hozzon létre, kódolja a funkciót, és fogadtassa el ezt a branchet a B csapattal, hogy az egyesítse (mergelje) a saját projektjébe. Az A csapat azonban nem kaphat felhatalmazást arra, hogy bármit megtehessen. A B csapatnak dokumentációt és segítséget kell nyújtania az A csapatnak a projekt szabványainak betartásához. A B csapat egyik tagjának át kell néznie az A csapat munkáját, és az integrációs tesztelés minden szokásos formáját el kell végeznie.
Az együttműködésnek ezt a formáját néha InnerSource-nak nevezik, mert hasonlít a nyílt forráskódhoz, de egyetlen szervezeten belül zajlik.