You are on page 1of 16

Rolul refactorizarii unui sistem informatic

pentru îmbunătăţirea fiabilităţii


Cuprins
Sistemele informatice ..................................................................................................................... 3
Definiție și componente .............................................................................................................. 3
Testarea sistemului informatic.................................................................................................... 5
Refactorizarea sistemelor informatice ........................................................................................... 6
Definiție ....................................................................................................................................... 6
Metode de refactorizare ............................................................................................................. 7
Avantajele refactorizarii .............................................................................................................. 8
Testarea de după refactorizare ................................................................................................... 9
Realizarea testelor de regresie.................................................................................................. 11
Exemplu de refactorizare a codului pentru limbajul JavaScript ............................................... 12
Bibliografie .................................................................................................................................... 16

2
Sistemele informatice

Definiție și componente
Un sistem informatic reprezintă o combinaţie de sisteme hardware, software,
infrastructura şi personal calificat, toate organizate astfel încât să faciliteze planificarea,
controlul, coordonarea şi luarea deciziilor în cadrul unei organizaţii. [1]
Mai mult, un sistem informatic reprezintă tehnologia informaţiei şi a comunicării (TIC) pe
care o organizaţie o utilizează, dar şi modul în care oamenii interacţionează cu această tehnologie
pentru suportul proceselor de business. [2] Aşadar, acesta permite captarea, transmiterea,
stocarea, aducerea, manipularea şi afişarea informaţiilor, având ca scop final producerea de
bunuri şi servicii pentru clienţi.
Tehnologia informaţiei a fost proiectată cu scopul de a reduce sarcinile oamenilor, care
de cele mai multe ori sunt nerealizabile de mintea umană, precum lucrul cu cantităţi mari de
informaţii, realizarea de calcule complexe, controlarea simultană a mai multor procese, etc.

Componentele de bază ale sistemelor informatice sunt:


1. Componente fizice (hardware)
Includ echipamente de intrare şi de ieşire, echipamente de stocare / procesare /
comunicare.
2. Componente software
Includ programe şi manuale de utilizare. Prin intermediul lor pot fi controlate sisteme
hardware.
3. Date
Datele sunt prelucrate de programe pentru producerea de informaţii utile.
4. Proceduri
Sunt politici care guvernează modul de operare al sistemelor hardware.
5. Oameni
Sistemele nu pot fi complet autonome, de aceea ele sunt operate şi menţinute de către
oameni cu o anumită calificare. Aceasta componentă poate influenţa cel mai mult
succesul sau eşecului unui sistem informatic.
6. Feedback
Este o componentă din ce în ce mai utilizată, întrucât, pe baza feedback-ului se menţin
sau se actualizează celelalte componente ale sistemului informatic şi deci se poate
îmbunătăţi fiabilitatea acestuia.

Sistemele informatice sunt sau pot fi utilizate pe toate treptele ierarhice ale unei
organizaţii.
Putem enumera astfel de sisteme: sisteme de procesare a tranzacţiilor, sisteme de
management, depozite de date (data warehouse), sisteme de planificare în afaceri (ERP), sisteme
expert, sisteme Informațional Geografice (GIS), automatizări birotice, etc.

3
Sistemele informatice bazate pe computere cuprind următoarele componente:
- Hardware
- Software
- Baze de date
- Reţele
- Proceduri

Primele patru componente reprezintă platforma tehnologiei informaţiei şi asigură măsuri de


siguranţă, managementul riscului şi a datelor.
Procedurile sunt comenzi pentru combinarea celorlalte componente pentru procesarea
informaţiei şi producerea rezultatelor dorite.

Mulți dezvoltatori de sisteme informatice folosesc proceduri precum System


Development Life Cycle (SDLC) ce implică secvenţe de Planificare, Analiză, Design, Creare,
Testare, Implementare, Mentenanţă şi Suport.

Exemplu de ciclu de viață cu 10 faze al unui sistem informatic [3]

Înaintea fazei de planificare trebuie realizat un studiu de fezabilitate pentru determinarea


dacă este necesară şi viabilă crearea sau îmbunătăţirea unui astfel de sistem. Acesta ajută la
determinarea costurilor, beneficiilor, resurselor, cât şi necesitaţilor utilizatorilor. Procesul de
dezvoltare poate continuă numai după ce managementul aprobă recomandările în urma studiului
de fezabilitate.

4
Componentele acestui studiu pot fi:
- Fezabilitate operaţională
- Fezabilitate economică
- Fezabilitate tehnică
- Fezabilitatea factorilor umani
- Fezabilitate juridică

Mediile în care sistemul informatic este dezvoltat reprezintă zone controlate în care
dezvoltatorii pot construi, distribui, instala, configura, testă şi execută componentele sistemului.
Aceste zone sunt specifice fiecărei etape din cadrul ciclului de viaţă al sistemului:
 Mediul de dezvoltare, în care dezvoltatorii pot lucra independent şi apoi să unească
acele componente;
 Mediul de integrare;
 Mediul de testare;
 Mediul de acceptare, în care părţile interesante (stakeholderii) pot testa sistemul
conform cerinţelor;
 Mediul de producţie, în care sistemul este lansat către utilizatorii finali.

Testarea sistemului informatic

În funcţie de tipul sistemului informatic, există diverse metode de testare:


 Testare unitară, folosind framework-uri precum Jasmine, Mocha, QUnit, Selenium
 Testare software automatizată, folosind Selenium, framework-uri xUnit, precum JUnit
 Teste de integrare
 Testare de sistem
 Black-box, include testare funcţională
 White-box
 Teste de acceptare
 Teste de regresie
 Teste de performanţă

Testele de performanţă măsoară performanţele reale ale sistemului, comparate cu cele


teoretice. Metrica performanţelor ce trebuie măsurate variază în funcţie de aplicaţie.
De exemplu, timpul de răspuns pentru o aplicaţie tip calculator matematic trebuie să fie de mai
puţin de o secundă în 90% din cazuri.
Performanţa poate fi măsurată urmărind timpul de răspuns, ieşirile sistemului şi utilizarea
resurselor. Dacă măsurătorile de performanţă sunt nemulţumitoare, atunci se iau măsuri pentru
îmbunătăţire: rescrierea codului, alocarea mai multor resurse, redesign de sistem, etc. [5]

Testele de fiabilitate măsoară capacitatea sistemului de a rămâne operaţional pentru


perioade lungi de timp. Fiabilitatea sistemului se exprimă, de obicei, în termeni de timp mediu
până la cedare (Mean Time To Failure - MTTF).

5
Pe măsură ce testăm sistemul, observăm erorile, încercăm să indeparam defectele şi să
continuăm testarea. În acelaşi timp, se înregistrează duratele de timp între eşecuri succesive.

Test-Driven Development este o mai mult o tehnică de dezvoltare decât de testare.


Dezvoltatorul poate realiza un nivel amănunţit de testare (precum Integration testing) folosind
TDD, în timp de un tester sau product owner creează teste de nivel înalt folosind Acceptance
Test-Driven Development (ATDD). Behavior-Driven Development (BDD) poate include ambele
tipuri de nivele de testare. [7]
TDD este o tehnică utilizată la dezvoltare de cod bazat pe cazuri de testare automată.
Testele sunt scrise înainte să fie scris codul. Testarea orientată pe teste include:
 Adăugarea unui test ce captează conceptul programatorului pentru funcţionarea dorită
a unei bucăţi mici de cod;
 Rularea testului, care ar trebui să pice din moment ce codul nu există încă;
 Scrierea codului şi rularea testului, într-o buclă, până când trece testul;
 Refactorizarea codului după ce trece testul;
 Repetarea procesului pentru următoarea bucată de cod, rulând atât testele anterioare
cât şi pe următoarele.

Cazurile de testare sunt în special la nivel de unitate şi concentrate pe cod (white-box), deşi
pot fi scrise şi la nivele de integrare sau de sistem. TDD a fost popularizat de Extreme
Programming (XP), dar este folosit şi în alte metodologii agile şi chiar în cicluri secvenţiale.
Test-Driven Development reduce apariţia defectelor, care sunt eliminate imediat ce apar,
ajutând programatorul să se concentreze pe rezultatele aşteptate. Testele sunt automate şi sunt
folosite la integrarea continuă.

Refactorizarea sistemelor informatice

Definiție

Refactorizarea este procesul de îmbunătăţire a structurii codului existent, fără să i se


schimbe comportamentul vizibil din exterior. Pentru a face schimbări majore în structura codului,
refactorizarea utilizează o succesiune rapidă de paşi mici, bine ştiuţi, care pot fi verificaţi fiecare
în parte ca fiind siguri şi de încredere, pentru păstrarea funcţionalităţii. Refactorizarea este des
utilizată în contextul Test-Driven Development/Design, în care este uşor de rescris prin teste
simple şi scalabile. [6]

6
Metode de refactorizare

Extract method
Metoda de extragere presupune mutarea unui bloc de cod într-o nouă metodă cu un
nume descriptiv, ceea ce face codul mai clar şi uşor de citit.
Pentru identificarea blocului care poate fi mutat, ne putem folosi de comentarii, de metode mult
prea lungi sau de adâncirea logicii, a structurilor repetitive sau condiţionale.

Inline method
Metoda inline este opusă metodei de extragere. Aceasta include corpul unei metode
care este chemată pentru a clarifica rolul său.
De obicei, metoda inline ajută să vedem structura mai mare a codului şi este un pas
intermediar în cadrul refactorizarii, incluzând metodă de extragere cu un domeniu puţin diferit.

Move method
Acest tip de refactorizare implică mutarea metodei într-o clasă diferită, actualizarea
referinţelor la noua locaţie a metodei şi asigurarea că parametrii corespund în continuare.
Metoda de mutare este crucială în menţinerea coeziunii şi în migrarea responsabilităţilor atunci
când va fi nevoie.
Accesul multiplu la datele clasei (foo.getA(), foo.getB(), foo.getC()), înseamnă adesea că
metodă care accesează datele (sau părţi din ea) ar trebui mutată in clasa cu datele respective.
De asemenea, metodele cu mulţi parametri sunt predispuse să fie mutate: dacă
majoritatea parametrilor aparţin de aceeaşi entitate, atunci metodă în sine ar putea fi relocata.
În cadrul metodei de mutare pot fi aplicate principii şi bune practici precum:
 Legea lui Demeter (caz specific de decuplare pentru programe orientate pe obiect)
 Tell, Don’t Ask
 Responsability Drive Development
 Principiul Singurei Responsabilităţi
 Coeziune ridicată / Cuplare slabă
 Încapsulare

Rename method
Metoda de redenumire este cel mai uşor tip de refectorizare şi implică găsirea tuturor
apelurilor respectivei metode şi redenumirea lor odată cu metodă în sine.
Este folosită atunci când scopul metodei nu este clar, corpul metodei nu este coerent cu
numele sau când metoda face mai mult să mai puţin decât sugerează numele.
Metoda ajută la:
 O mai bună urmărire şi înţelegere a codului: denumirile mai lungi pot indica scopul său
pot fi prea ostentative, pe când denumirile scurte pot să nu explice suficient metoda.
 Un singur scop: numele ar trebui să capteze tot ce face metoda (la un anumit nivel de
abstractizare); altfel, ar trebui să realizăm o altfel de refactorizare (precum metoda de
extragere) pentru a menţine un cod curat.

7
DRY
O tehnică de refactorizare des întâlnită este eliminarea duplicatelor folosind principiul
Don’t Repeat Yourself (DRY). Fiecare parte a codului trebuie să aibă o singură şi autoritară
reprezentare în cadrul sistemului.
Principiul DRY este utilizat la:
 Înlăturarea multiplelor reprezentări a informaţiei
 Înlăturarea din schiţe a duplicatelor neintenţionate
 Ştergerea codului „copy / paste”
 Susţinerea reutilizării codului
După aplicarea acestei tehnici, se poate face trecerea de la cod specific la un cod generic
pentru folosirea în mai multe situaţii.

De la specific la generic
Codul specific tinde să fie fragil când vine vorba de schimbări pe viitor. Acesta trebuie să
poată fi schimbat uşor conform cu acţiunile din jurul său şi cu codul HTML. Cu cât lucrăm mai
mult, ne dăm seama ce înseamnă să scriem un cod generic, şi cu toate acestea trebuie găsită o
balanţă între specific şi generic pentru a fi citit şi înţeles rapid.
Pe măsură ce testele devin mai specifice, codul devine tot mai generic.

Avantajele refactorizarii
În urma refactorizarii apar următoarele avantaje:
 Se îmbunătăţeşte lizibilitatea codului
 Se reduce costul la schimbarea codului
 Se îmbunătăţeşte performanţa (timpi de execuţie, capacitate de stocare)
 Se pregăteşte pentru viitoare schimbări
 Se înţelege design-ul sau se clarifică funcţionalitatea

Lucruri care cresc costul schimbării de cod:


 Duplicatele, atât la nivel de bloc de cod, cât şi la nivel de algoritmi
 „code smells”
Lucruri care scad costul schimbării de cod:
 Refactorizare acum, faţă de mai târziu
 Cuplare slabă (decuplare)
 Metode scurte
 Fără comentarii
 Design simplu
 Structura coerentă
 Paşi mici
 Instrumente de refactoriare automată în cadrul IDE-ului
 Folosirea de design patterns

8
Testarea de după refactorizare
Refactorizarea este o parte fundamentală a metodologiei Agile. Aceasta înseamnă
rescrierea de mici porţiuni de cod care să fie echivalent din punct de vedere funcţional, dar de o
mai bună calitate. Refactorizarea se realizează după ce codul este testat, însă după refactorizare
trebuie testată din nou funcţionalitatea. [4]
În timpul refactorizarii, dezvoltatorii schimbă, de regulă, mai mult decât complexitatea şi
calitatea codului. Astfel, poate fi o modificare destul de mică, însă aceasta se reflectă şi în
celelalte părţi ale aplicaţiei. De aceea trebuie realizată teste de regresie după modificarea codului
propriu-zis.
În practică, codul care trebuie refactorizat poate să aibă:
- Zero teste unitare
- Teste unitare slabe
- Teste unitare bune

După realizarea refactorizarii este important să se apeleze la testele de regresie pentru a


asigura menţinerea atât a părţilor funcţionale, cât şi a celor non-functionale ale sistemului.
Testele sunt considerate o plasă de siguranţă sub orice refactorizare şi orice posibilă schimbare
în logica aplicaţiei.

Testare cod
curent
Stabilit ce
trebuie testat

Refactorizare

Test de
regresie

Testare cod
rescris

Nu se creează teste noi la nivel de regresie, ci se selectează teste dintre cele existente şi
sunt executate pentru a asigura că nu este nimic defect în noua versiune a produsului software.
Ideea testării pentru regresie este verificarea faptului că niciun defect nu a fost introdus
în porţiunea nemodificată a sistemului, datorită schimbărilor aduse în alte părţi ale sistemului. În
timpul testării sistemului sunt descoperite multe defecte, iar codul este modificat pentru a le
repara.
Testele de regresie se efectuează atunci când produsul său mediul au fost schimbate. Din
motive de costuri şi de program, acestea se pot automatiza.

9
Testele unitare se ocupă de secţiuni mici de cod. În mod ideal, fiecare test este
independent, iar spioni (spies), variabile şi funcţii fictive (mocks) sunt folosite pentru a obţine
control asupra mediului de lucru. Refactorizarea se poate face pe acele bucăţi mici de cod.

Refactorizarea codului când nu există teste unitare


Atunci când se lucrează cu cod învechit, în general nu există teste unitare. Însă nu se poate
trece imediat la refactorizare. Mai întâi se adaugă teste unitare asupra codului existent. După
refactorizare, aceste teste ar trebui să se menţină valide. În acest mod se menţine
mentenabilitatea codului, precum şi calitatea acestuia.
Acest lucru, însă, este unul complex. Mai întâi trebuie găsită funcţionalitatea codului. Apoi
trebuie realizate cazurile de testare care să acopere funcţionalitatea. Pentru găsirea
funcţionalităţii, trebuie date câteva date de intrare şi observate ieşirile. Echivalentă funcţională
este demonstrată în momentul în care intrările şi ieşirile sunt conforme cu cele originale.

Refactorizarea pentru creşterea calităţii testelor unitare existente


Putem, de asemenea, să identificăm cod cu teste unitare de calitate inferioară. Spre
exemplu, cele care verifică multiple scenarii la un loc. Aceasta se întâmplă din cauza nedecuplarii
corespunzătoare a codului (Figura 1).

Figura 1. Cod unitar ce depinde de mediu (xunitpatterns.com)

Acest mod nu este de dorit deoarece testul nu trebuie să depindă de starea mediului.
O soluţie este refactorizarea codului care să ofere suport pentru dependente
substituibile, aşa cum este prezentat în figură 2. Aceasta permite folosirea de obiecte de tip

10
mock sau stub. Testul unitar este împărţit în trei teste unitare ce testează separat trei scenarii.
Codul rescris are acum un provider configurabil pentru timp, independent de platformă
(mediu), iar cele trei teste acoperă cazurile de execuţie posibile.

Figura 2. Teste unitare independente (xunitpatterns.com)

Tratarea testelor unitare ca și cod


Situaţia din figura 2 se ocupă de o parte de cod care are teste unitare corespunzătoare.
Însă, refactorizarea nu este suficientă, ci trebuie verificată şi validitatea testelor, căci şi acestea
reprezintă tot cod. Fiecare acţiune de refactorizare trebuie să includă o verificare şi eventual o
refactorizare a testelor în sine.

Realizarea testelor de regresie


După ce codul a fost testat unitar, trebuie verificată soluţia în cadrul contextului. În
metodologia Agile trebuie adăugată mereu valoare. Pentru aceasta, testele trebuie să
performeze conform business-ului.
Un test de regresie este proiectat să testeze fluxurile importante din cadrul soluţiei, iar
aceste fluxuri adaugă valoare business-ului. În funcţie de riscuri şi de scalabilitatea testului de
regresie, acesta se poate adăuga sau nu.

11
Crearea unui test de regresie scalabil
Cazurile de utilizare reprezintă un mod utilizat de a descrie părţi mici din funcţionalitate,
astfel încât să partiţioneze testul de regresie. Astfel, se creează un set mic de teste de regresie
pentru a acoperi câte un caz de utilizare. Când o secţiune de cod este schimbată, se poate vedea
uşor cărui caz îi aparţine şi apoi se execută testele de regresie corespunzătoare cazului.
Cu toate acestea, când codul este reutilizat (good practice), se ţinteşte un grup de cazuri
de utilizare. Se pot utiliza mindmaps pentru gestionarea dependinţelor în cadrul proiectului.
Acestea oferă o vizibilitate mai bună între bucata de cod şi cazul său de utilizare.

Extinderea domeniului pentru testul de regresie


În teorie, se testează să nu existe arii afectate. Deci, se testează partea care este afectată
de refactorizare, cât şi căile principale din cadrul soluţiei, mai ales cele care sunt mai importante
pentru client.
Aşadar, fiecare schimbare a codului necesită testare. Astfel, testarea pe diferite nivele
este obligatorie după refactorizare.

Exemplu de refactorizare a codului pentru limbajul JavaScript

Într-un thread de pe forumul SitePoint, o parte din cod trebuia să controleze când anume
un alt dropdown box să fie vizibil, mai exact atunci când Location 5 este selectat. [8]

Acesta este codul HTML:


<select id="location">
<option value="loc1">Location 1</option>
<option value="loc2">Location 2</option>
<option value="loc3">Location 3</option>
<option value="loc4">Location 4</option>
<option value="loc5">Location 5</option>
<option value="loc6">Location 6</option>
<option value="loc7">Location 7</option>
</select>
<select id="second">
<option value="OPT1">Option 1</option>
<option value="OPT2">Option 2</option>
<option value="OPT3">Option 3</option>
<option value="OPT4">Option 4</option>
</select>

Acesta este codul original CSS:


#second { display: none; }
#second.show { display: block; }

12
Iar acesta este codul original JavaScript:
document.getElementById("location").onchange = function () {
if (this[this.selectedIndex].value === "loc5") {
document.getElementById("second").className = "show";
} else {
document.getElementById("second").className = "";
}
};

Observând codul iniţial, ne putem da seama ce se poate îmbunătăţi:


- Stocarea string-urilor în variabile, pentru a le grupa într-un singur loc.
- Event handler-ul onchange poate fi suprascris, aşa că vom folosi addEventListener.
- Proprietatea className va suprascrie numele clasele existente, deci vom folosi în
schimb classList.

Folosirea variabilelor pentru prevenirea duplicatelor


Id-ul „location” al dropdown-ului şi opţiunea cu valoarea „loc5” trebuiesc puse în acelaşi
loc. Totodată, observăm că al doilea element <select> este chemat de două ori
(document.getElementById("second")), deci putem deja să creăm o nouă variabilă pentru el:

// cod imbunatatit
var source = document.getElementById("location");
var target = document.getElementById("second");
var triggerValue = "loc5";
source.onchange = function () {
var selectedValue = this[this.selectedIndex].value;
if (selectedValue === triggerValue) {
target.className = "show";
} else {
target.className = "";
}
};

Îmbunătăţirea Event Handlerelor


Atunci când este setat un event handler folosind proprietatea onChange, se va suprascie
handler-ul anterior pentru acelaşi event:
// bad code
source.onchange = function () {
// ...
};

Testele sunt o bună metodă de a verifica comportamentul codului în diferite situaţii şi de


a-l corecta. Am folosit framework-ul Jasmine, ce utilizează funcţii globale it, cu o descriere a
„spec-ului” respectiv.
Acestea sunt primele două teste care, la rulare, vor trece:

13
it("should add the 'show' class name when the 'loc5' option is selected",
function() {
changeSelectTo(source, "loc5");
expect(target.classList.contains("show")).toBe(true);
});

it("should remove the 'show' class name when an option value different from
'loc5' is selected", function() {
changeSelectTo(source, "loc2");
expect(target.classList.contains("show")).toBe(false);
});

Funcţia changeSelectTo alterează valoarea elementului <select> şi „expect”-ul că


elementul are clasă potrivită pentru a apărea („show”) sau dispărea. Însă, odată ce onchange
este alterat, funcţia care va schimba clasa se va pierde.
Astfel, testul de mai jos va pica:
it("should toggle the class name even when the onchange event is replaced",
function () {
changeSelectTo(source, "loc2");
expect(target.classList.contains("show")).toBe(false); // trece

// Suprascrierea handler-ului onchange


source.onchange = function doNothing() { return; };

changeSelectTo(source, "loc5");
expect(target.classList.contains("show")).toBe(true); // pica
});

De aceea, vom inlocui onchange cu addEventListener:


// good code
source.addEventListener("change", function (evt) {
// ...
}, false);

Imbunătățirea claselor
O altă problemă a codului inițial este că elementul <select> va pierde clasele anterioare
deoarece className va înlocui tot ce era inainte:
// bad code
target.className = "show";

În testul de mai jos ne așteptăm ca o clasă indent să apară în continuare pe al doilea


element <select>:
it("should retain any existing class names that were on the target element",
function () {
changeSelectTo(source, "loc2");
target.classList.add("indent");
expect(target.classList.contains("indent")).toBe(true);

14
changeSelectTo(source, "loc5");
expect(target.classList.contains("indent")).toBe(true); // pica
});
Însa, className înlocuiește toate valorile clasei și astfel codul va pica.
În schimb, putem folosi classList pentru adăugarea și ștergerea numelor de clasă:
// good code
target.classList.add("show");
// ...
target.classList.remove("show");
Dacă trebuie suportat browserul IE9 sau mai vechi, se pot folosi funcțiile addClass sau
removeClass pentru rezultate similare.

După toate aceste îmbunatățiri, codul JavaScript refactorizat va fi urmatorul:


// dupa refactorizare
var source = document.getElementById("location");
var target = document.getElementById("second");
var triggerValue = "loc5";

source.addEventListener("change", function () {
var selectedValue = this[this.selectedIndex].value;
if (selectedValue === triggerValue) {
target.classList.add("show");
} else {
target.classList.remove("show");
}
}, false);

Iar mai jos au fost rulate testele unitare realizate cu framework-ul Jasmine:

În concluzie, îmbunătățirea codului nu trebuie sa fie un lucru dificil. Aplicarea principiului


de la specific la generic are un rol benefic odată cu dezvoltarea orientată pe testare.
Prin refactorizarea se obține cod mai flexibil și mai „future proof”, fără să fie nevoie de
revenire pentru refacerea codului.

15
Bibliografie
[1] http://www.businessdictionary.com/definition/information-system.html
[2] https://en.wikipedia.org/wiki/Information_system
[3] https://en.wikipedia.org/wiki/Systems_development_life_cycle
[4] Jeroen Mengerink, How to Test Refactoring, www.AgileRecord.com
[5] http://www.shiva.pub.ro/PDF/TEST/Teste_functionale_curs_6.pdf
[6]https://sites.google.com/site/agileskillsprojectwiki/agile-skills-inventory/technical-
excellence/refactoring
[7]https://www.istqb.org/news/news/2014/189-in-chapter-3-1-1-in-agile-tester-extension-test-
driven-development.html
[8] https://www.sitepoint.com/javascript-refactoring-techniques-specific-to-generic-code

16