This shows you the differences between two versions of the page.
icalc:laboratoare:laborator-07 [2022/04/15 00:03] theodor.ungureanu |
— (current) | ||
---|---|---|---|
Line 1: | Line 1: | ||
- | ===== Laborator 07 - Test-driven development ===== | ||
- | |||
- | <note warning> | ||
- | Best to have: **InteliJ** | ||
- | * Instalare: https://ocw.cs.pub.ro/courses/poo-ca-cd/resurse-utile/instalare-intellij-idea | ||
- | * Activare: https://ocw.cs.pub.ro/courses/poo-ca-cd/resurse-utile/activare-intellij-idea | ||
- | </note> | ||
- | |||
- | |||
- | <hidden> | ||
- | |||
- | ==== Introducere ==== | ||
- | |||
- | {{:icalc:laboratoare:07:lab7-joke.jpeg?200|}} | ||
- | |||
- | De cele mai multe ori, dupa ce un programator finalizeaza scrierea unui "program" dupa anumite specificatii acesta este trimis catre echipa de testare in vedere gasirii bugurilor. Daca sunt gasite probleme "programul" se intoarce in faza de dezvoltare pentru a fi remediate. Acest schimb intre programator si echipa de testare se poate face de un numar suficient de mare de ori pana cand toate problemele sa fie rezolvate. Pentru a evita situatia enumerata mai sus se poate folosi tehnica TDD prin care programatorul scrie inainte teste inainte de a implementa o anumita functionalitate. | ||
- | |||
- | === Ce inseama TDD? === | ||
- | |||
- | {{:icalc:laboratoare:07:lab7-tdd.png?400|}} | ||
- | |||
- | TDD (Test Driven Development) este o abordare a dezvoltarii de software prin care testele sunt scrise inaite ca softul sa fie scris. | ||
- | |||
- | Aceasta metoda contine 3 pasi: | ||
- | - Ordered List Item se scrie un test care sa pice | ||
- | - se scrie minimul de cod necesar pentru a face testul sa treaca | ||
- | - se curata codul scris si se refactorizeaza daca este nevoie | ||
- | |||
- | |||
- | === Cum pregatim proiectul? === | ||
- | | ||
- | In cadrul acestui laborator vom face de la 0 un proiect si vom urma toti pasii necesari pentru a dezvolta software folosind TDD. | ||
- | * Folosind interfata IntelliJ vom face un nou proiect denumit Laborator7 | ||
- | |||
- | {{ :icalc:laboratoare:07:project1.png?400|}} | ||
- | {{:icalc:laboratoare:07:project2.png?400 |}} | ||
- | |||
- | <note> | ||
- | Pentru a separa partea de cod de partea de testare vom face 2 package-uri. In package-ul denumit **Code** vom adauga clasele ce vor fi folosite de software, iar in package-ul **TestingModule** vom adauga partea de testare. | ||
- | {{ :icalc:laboratoare:07:project3.png?400 |}} | ||
- | </note> | ||
- | |||
- | * Pentru a scrie prima functie de testare vom merge in package-ul **TestingModule** si vom face un nou fisier de tipul **Java Class** | ||
- | |||
- | {{ :icalc:laboratoare:07:project4.png?400 |}} | ||
- | |||
- | |||
- | * In cadrul acestui fisier facem o clasa in interiorul careia ne vom scrie testele. Crearea unui test se realizeaza prin anotarea unei metode cu **@Test**. | ||
- | <note> | ||
- | In cazul in care IntelliJ nu importa automat modulele necesare putem sa le adaugam manual asa cum este prezentat in poza. Desigur, putem adauga **JUnit in IntelliJ** asa cum am invatat si in laboratorul trecut. | ||
- | {{ :icalc:laboratoare:07:project5.png?300 |}} | ||
- | </note> | ||
- | |||
- | === Specificatiile proiectului === | ||
- | In cadrul laboratorului de astazi vom face un program denumit sugestiv **SplitBill**. Acesta va lua anumite sume de bani, persoane, procentul de tips si va returna o suma de bani ce trebuie data de fiecare persoana pentru achitarea notei. | ||
- | |||
- | |||
- | === Cerinta 1 === | ||
- | == Etapa 1: writing a failing test == | ||
- | |||
- | * Prima cerinta a proiectului este scrierea unui test **getTotalAmountWithZeroTips()**. In cadrul acestei functii vom lua o instanta a calculatorului si vom returna rezultatul unei metode **getTotalAmount(double amount, double tips)**. Aceasta va adauga procentul de tips la suma si va returna suma totala ce trebuie platita, suma ce contine si tips-ul. | ||
- | |||
- | <note> | ||
- | IntelliJ ne ajuta si ne face highlight pe ce nu exista. Astfel ne putem folosi de editor pentru a face clasa Calculator si a adauga in ea lucrurile necesare. | ||
- | |||
- | {{ :icalc:laboratoare:07:cerinte1.png?300|}} | ||
- | {{:icalc:laboratoare:07:cerinte2.png?300|}} | ||
- | </note> | ||
- | |||
- | Primul din cei 3 pasi TDD a fost realizat: testul a fost scris. Dupa ce rulam acest test vom vedea faptul ca testul nu va trece intrucat noi nu avem implementata aceasta metoda in cadrul clasei. | ||
- | Urmatorul pas este scrierea celor mai simple linii pentru a face testul sa treaca. | ||
- | |||
- | {{ :icalc:laboratoare:07:cerinte3.png?800 |}} | ||
- | |||
- | == Etapa 2: make the test pass == | ||
- | |||
- | Avand in vedere ca testul nostru este conceput pentru cazul in care tips-ul este 0 ceea ce avem de facut este sa returnam suma initiala: | ||
- | <code java> | ||
- | public class Calculator { | ||
- | public double calculate(double amount, double tips) { | ||
- | return amount; | ||
- | } | ||
- | } | ||
- | </code> | ||
- | |||
- | Daca rulam testul inca odata vom vedea ca acesta va trece: | ||
- | {{ :icalc:laboratoare:07:cerinte4.png?800 |}} | ||
- | |||
- | == Etapa 3: refactoring == | ||
- | Pentru testul curent, dat fiind faptul ca este foarte simplu vom omite etapa refactoring considerand ca nu avem ce modifica. | ||
- | |||
- | === Cerinta 2 === | ||
- | == Etapa 1: writing a failing test == | ||
- | * A doua cerinta este scrierea unui test **getTotalAmountWithTips()**. De data aceasta avem ca date de intrare **amount 78** si **tips 10** si ne asteptam ca functia sa returneze 85.8. | ||
- | |||
- | <code java> | ||
- | @Test | ||
- | public void getTotalAmountWithTips() { | ||
- | Calculator calculator = new Calculator(); | ||
- | assertEquals(85.8, calculator.calculate(78, 10)); | ||
- | } | ||
- | </code> | ||
- | |||
- | == Etapa 2: make the test pass == | ||
- | |||
- | <code java> | ||
- | public class Calculator { | ||
- | public double calculate(double amount, double tips) { | ||
- | if (tips == 0.0) { | ||
- | return amount; | ||
- | } | ||
- | else { | ||
- | var tipsAmount = amount / 100 * tips; | ||
- | return amount + tipsAmount; | ||
- | } | ||
- | } | ||
- | } | ||
- | </code> | ||
- | |||
- | == Etapa 3: refactoring == | ||
- | De data aceasta putem face refactoring eliminand din codul redundant: | ||
- | <code java> | ||
- | public class Calculator { | ||
- | public double calculate(double amount, double tips) { | ||
- | var tipsAmount = amount / 100 * tips; | ||
- | return amount + tipsAmount; | ||
- | } | ||
- | } | ||
- | </code> | ||
- | |||
- | |||
- | === Tips and Tricks === | ||
- | JUnit ne permite sa dam argumente functiilor de testare. | ||
- | Folosind adnotarea **@ValueSource** putem adauga un array de elemente pe care sa le folosim ca argumente. | ||
- | <code java> | ||
- | @ParameterizedTest | ||
- | @ValueSource(doubles = {0, 10, 100, 78, 20}) | ||
- | public void getTotalAmountWithTips2(Double amount) { | ||
- | Calculator calculator = new Calculator(); | ||
- | System.out.println("Amount " + amount + " with tips 10 = " + calculator.calculate(amount, 10)); | ||
- | } | ||
- | </code> | ||
- | |||
- | Folosind adnotarea **@CsvSource** putem da mai multe argumente functiilor de testare. | ||
- | <code java> | ||
- | @ParameterizedTest | ||
- | @CsvSource({ | ||
- | "0, 0", | ||
- | "10, 11", | ||
- | "100, 110", | ||
- | "78, 85.8", | ||
- | "20, 22", | ||
- | }) | ||
- | public void getTotalAmountWithTips3(Double amount, Double expected) { | ||
- | Calculator calculator = new Calculator(); | ||
- | assertEquals(expected, calculator.calculate(amount, 10)); | ||
- | } | ||
- | </code> | ||
- | <note important>Este important de mentionat faptul ca atunci cand se folosesc adnotarile mentionate mai sus trebuie sa folosim **@ParameterizedTest** in loc de **@Test**.</note> | ||
- | |||
- | ==== Exercitii ==== | ||
- | |||
- | Descarcati arhiva de aici: {{:icalc:laboratoare:07:laborator7.zip|}}. | ||
- | |||
- | **Partea 1:** | ||
- | - Sa se scrie un failing test **testWithMultipleAmount** ce va lua un array de amount-uri (double) si va returna suma ce contine si tipsul. | ||
- | - Sa se scrie codul (si sa se refactorizeze daca este nevoie) pentru a face testul de la pasul anterior sa treaca. | ||
- | - Sa se scrie un test ce verifica faptul ca daca exista sume negative sau procentul de tips este negativ atunci sa arunca o eroare. | ||
- | - Sa se scrie codul (si sa se refactorizeze daca este nevoie) ce face testul mentionat mai sus sa treaca. | ||
- | - Sa se scrie un test pentru valorile de intrare **amount = 110**, **tips = 10** si **personsNumber = 2** rezultatul va fi **expected = 55**. | ||
- | - Sa se scrie codul necesar pentru a face testul mentionat mai sus sa treaca. (trebuie adaugat suport pentru un parametru in plus si anume numarul de persoane intre care trebuie impartita nota). | ||
- | - Sa se scrie un test folosind adnotarea **@CsvSource** prin care sa se testeze 5 cazuri de intrare diferite modificand toti parametrii de intrare. | ||
- | |||
- | **Partea 2:** | ||
- | - Creati un proiect nou sau folositi scheletul din cadrul primei parti. | ||
- | - Sa se scrie un test ce verifica daca suma numerelor dintr-un string gol este 0. | ||
- | - Sa se implementeze functia ce face testul sa treaca (si sa se refactorizeze daca este nevoie). | ||
- | - Sa se scrie o functie de test ce verifica faptul ca suma cu stringul de intrare **"1 2 3 10 4 7"** este **27**. (numerele sunt separate prin exact un spatiu.) | ||
- | - Sa se implementeze codul ce face testul de mai sus sa treaca (si sa se refactorizeze daca este nevoie). | ||
- | - Sa se scrie o functie ce testeaza folosirea "," ca si delimitator in loc de spatiu. | ||
- | - Sa se scrie codul ce face testul de mai sus sa treaca (si sa se refactorizeze daca este nevoie). | ||
- | - Sa se scrie o functie de test ce permite folosirea literelor (mari si mici) si semnelor de punctuatie ca si delimitatori. Oricat de multe intre numere. | ||
- | - Sa se implementeze codul ce face testul de mai sus sa treaca (si sa se refactorizeze daca este nevoie). | ||
- | <note tip>Refactorizare: folositi regex</note> | ||
- | |||
- | ==== Resurse utile ==== | ||
- | * [[https://www.jetbrains.com/help/idea/tdd-with-intellij-idea.html/]] | ||
- | * [[https://www.baeldung.com/parameterized-tests-junit-5/]] | ||
- | |||
- | </hidden> | ||