5. In an ideal world...
• Frequency: how rapidly do we want our
feedback ?
• Fidelity: how accurate do we want the
red/green signal to be?
•Urgency: how fast do we need to deploy
• Overhead: how much are we prepared to
pay?
6. Feedback
• Have I broken anything?
• Is my code base healthy?
• Is the software doing something useful for
the user?
It should NOT help you identify the exact line
number where the test broke
7. How I did it:
For every project:
• For every class library - a unit test project
• For every class - a new test class
• For every public method - at least one test
method
• Each class tested in isolation
• All class dependencies were abstractions for mocking
• Code coverage >= 80%
8. Pros and cons
• Small implementation
changes broke dozens of
tests – hard to refactor
• Difficult to understand
tests intent
• More test code
than implementation code
• Hard to convince
managers, colleagues,
clients
• A lot of effort to test first
• Useless tests
• High confidence
• Less bugs
• SOLID, Ioc, coupling
11. We unit test at
the port, driving
the application
domain
Don’t test the
adapter
Integration test
confirm hookup
of port to adapter
The port is
a behaviour
boundary
System Tests
exercise the
boundary
13. Mock other
ports
Don’t mock
adapters -
mock ports
Don’t mock
things we
don’t own
Don’t mock
internals
they come
from
refactoring
Mock other
public
classes –
they are
part of our
API
20. Summary
• The reason to test is a new behavior, not a method
on a class
• Don’t test internals
• Don’t make everything public in order to test it
• Write dirty code to get green, then refactor
• No new tests for refactored internals and privates
(methods, classes)
• Both Develop and Accept against tests written on a
port
• Add Integration tests for coverage of ports to
adapters
• Add system tests for end-to-end confidence
• Don’t mock internals, privates, or adapters
21.
22.
23. Please fill the online evaluation form after event
The Zen of Test Driven Development
Mihai Mahulea
BizPro Technologies
[25th of October 2014]
Editor's Notes
Voi incepe cu sumarul prezentarii.
Mesajut principal pe care vreau sa-l transmit este acesta: Nu testati clase sau metode testati functionalitate.
Adica:
In trecut, cand practicam tdd imi ziceam:”Urmeaza sa implementez metoda asta noua in clasa asta,deci ar trebui sa scriu un test nou pentru aceasta metoda.” Gresit. Trigerul scrierii unui unit test nou nu ar trebui sa fie crearea unei clase noi sau crearea unei metode publice noi. Acest triger ar trebui sa fie implementarea unei functionalitati noi, a unui compartament nou.Requirement-urile ar trebui sa ghideze testele.
Metoda de testare ar trebui sa fie „Outside in”. Adica detaliile de implementare nu ar trebui sa fie testate. Nu ma refer sub nici o forma la la testare aplicatiei prin intermediul UI-ului, ma refer la layer-ul public, la API-ul public al aplicatie.
Nu scrieti teste care acopera detalii de implemetare.
Incapsularea este solutia multor probleme din programare. O astfel de problema ar fi faptul ca testele sunt cuplate de detalii, detin mult prea multe informatii despre modul in care am implementat anumite use case-uri.
Pentru fiecare bucata de software pe care o scriem ar trebui sa identificam API-il public. Aici nu ma refer la API-ul REST sau SOAP , ma refer la un numar de clase care lucreaza impreuna pentru a oferi un serviciu.
Nu toate clasele unui asembly ar trebui sa fie publice si mai ales nu ar trebui sa fie facute publice doar pt a fi testate.
Pentru a identifica API-ul public trebuie sa ne gandim la ce functionalitati ofera soft-ul nostru.
Modul in care aceste fuctionalitati sunt implementate ar trbui sa fie incapsulate si testele nu ar trebui sa stie de ele.
Daca rezolvam aceasta problema, ulterior cand vom refactoriza implementarea, testele nu vor fi afectate.Deasemenea veti scrie un numar redus de teste.
Desi titlul prezentarii se refera la Test Drivren Development, consider ca notiunea de „Self-Testing Code ” reprezinta esenta prezentarii.
„Self-Testing Code ” este practica de a scrie teste automate cuprinzatoare in conjuctie cu soft-ul functional.
In alte cuvinte, nu ai terminat de implementat un user story, nu ai rezolvat un bug, pana cand nu ai scris teste automate.
TDD-ul este o modalitate a atinge „Self-Testing Code ”. Nu este singura.
Cel mai mare beneficiu al codului de auto-testare nu este numarul mic de buguri din productie. Acesta este Increderea. Incredera pe care o capeti atunci cand fac schimbari in sistem.
Aceasta incredere este un factor cheie al procesului de Continous Integration si Continous Delivery.
Intr-o lume ideala am avea un feedback instant, infailibil despre deciziile noastre de programare. Dupa fiecare tasta apasata, daca codul este gata, am face deploy pe loc.
Acest lucru este imposibil pe moment asa ca intrebarea este cat de departe vrem sa fim de acest deziderat.
Deciziile care implica codul de auto-testare, TDD sunt strans legate de anumite contrangeri si compromisuri.
Frecventa: cat de rapid dorim feedback-ul
Fidelitate: cat de siguri vrem sa fim ca intreg sistemul functioneaza corect
Efort: cat timp suntem dispusi sa investim
Urgenta: cat de repede dorim codul/bugfixul in productie
Durata de viata a proiectului: cat timp va exista soft-ul in productie
Obiectivul meu este de a intelege acest set de compromisuri prin articularea lor la oamenii care sunt pregatiti sa le coombata intr-un mod constructiv.
Nu exista o reteta fixa pentru a scrie teste.
Trebuie sa punem in balanta multi factori inainte de a lua o decizie, iar corectitudinea deciziilor creste odata cu experienta.
O suita buna de unit teste ar trebui sa ne ofere feedback calitativ asupra urmatoarelor aspecte:
De cate ori implementam o functionalitate noua sau rezolvam un bug am dori sa fim anuntati daca am introdus alte bug-uri noi. De asta notiunea de cod care se auto-testeaza e asa de importanta. Testele automate raman in solutie si ofera increderea ca nu am stricat functionalitatea testata.
Daca codul meu este sanatos. Voi putea adauga functionalitati noi, face refactoring, bugfixing cu usurinta dupa perioada asta de timp?
3. Daca soft-ul ofera functionalitatea dorita utilizatorului. Daca acopera nevoile clientilor si daca acestea sunt satisfacute. Daca testam doar clase si metode in izolare nu puteam acoperi nevoile adevarate ale userilor.
Daca la inceputul carierei eram de parere ca un cod bun este un cod testabil pe parcurs mi-am schimbat parerea.
Am scris si vazut cod testabil si testat care numai bun nu era. Code review-urile, pair programing-urile sunt mult mai valoroase in a asigura calitatea codului. Nu ne putem baza doar pe acest fapt, daca poate fi testat sau nu?
Principalul motiv al problemelor mele a fost ca am inteles gresit termenul de „unit”.
In cartea originala Kent Beck admite ca posibil sa faca o greseala refolosind termenul de „unit test”
Am crezut ca unit-ul este o clasa care trebuie testata in izolare. ‘
Should be rarely used
A good place to use them is to mock the technology implementations of ports
Another place is to mock some other public API calls
In some cases I mocked internal code that couldn’t be tested.
Don’t mock internals.
Address the misunderstanding of Outside In testing. It relies heavily on mock usage.
The dopamine release that comes from the flow, is from the step of going from red to green, it's not the refactor step.
The test are the top level of authority in explaining the system and dictating what is right and combined with the notion of the red - green is the important part of red-green-refactor
Leeds a lot of times to under-refactoring, under code clarity,
In the Green step you should do the simplest thing to make it pass. You should do bad engineering, copy paste code, have 400 lines of code in a method in order to make you test pass.
You can’t figure out what the algorytm to solve a problem and figure out what’s the engineering for that algorytm is in order to make it maintanable.
Separate the two problems.
The refactoring step in when we produce clean code
when we apply patterns
remove dupplication
sanitaze the code smells
We do no write new unit test here.
Because I haven’t written all my code in the green step, without any new test, just written an answer, I didn’t realized that at this point when I actually produce classes and patterns to actually implement some details I didn’t need any new tests for those classes.
There are allready covered by tthe original test.
Refactoring steps are safe
Code coverage might be usefull here
You have the behaviour preserved in the original tests and for the rest of the code base lifetime you can keep refactoring.