Cum testați / modificați codul netestat și netestabil?

În ultima vreme am trebuit să schimb un cod pe sistemele mai vechi, unde nu tot codul are teste unitare Înainte de a face schimbările, vreau să scriu teste, dar fiecare clasă a creat o mulțime de dependențe și alte anti-modele care au făcut testele destul de greu. Evident, am vrut să refactor codul pentru a face mai ușoară testarea, scrierea testelor și apoi modificarea acestora Așa ai face? Sau ați petrece mult timp scriind testele greu de scris, care ar fi în mare parte eliminate după ce refactorizarea va fi finalizată?

0
fr hi bn

4 răspunsuri

Mai întâi, aici este un articol grozav cu sfaturi despre unitatea de testare . În al doilea rând, am găsit o modalitate excelentă de a evita să faceți tone de schimbări în codul vechi, doar să refacționați puțin până când îl puteți testa. O modalitate ușoară de a face acest lucru este de a face membrii privați protejați și apoi să înlocuiți câmpul protejat.

De exemplu, să presupunem că aveți o clasă care încarcă unele lucruri din baza de date în timpul constructorului. În acest caz, nu puteți suprascrie doar o metodă protejată, dar puteți extrage logica DB într-un câmp protejat și apoi o suprascrieți în test.

public class MyClass {
    public MyClass() {
        // undesirable DB logic
    }
}

devine

public class MyClass {
    public MyClass() {
        loadFromDB();
    }

    protected void loadFromDB() {
        // undesirable DB logic
    }
}

și apoi testul dvs. arată astfel:

public class MyClassTest {
    public void testSomething() {
        MyClass myClass = new MyClassWrapper();
        // test it
    }

    private static class MyClassWrapper extends MyClass {
        @Override
        protected void loadFromDB() {
            // some mock logic
        }
    }
}

Acesta este oarecum un exemplu rău, pentru că ați putea folosi DBUnit în acest caz, dar de fapt am făcut acest lucru într-un caz similar recent, deoarece am vrut să testez câteva funcționalități care nu au legătură cu datele încărcate, deci a fost foarte eficientă. De asemenea, am descoperit o astfel de expunere a membrilor pentru a fi folositoare în alte cazuri similare în care trebuie să scap de o dependență care a fost într-o clasă de mult timp.

Aș recomanda împotriva acestei soluții dacă scrieți un cadru, totuși, dacă nu vă deranjează expunerea membrilor la utilizatorii din cadrul dvs.

Este un pic de hack, dar mi sa părut util.

0
adăugat

@valters

Nu sunt de acord cu afirmația ta că testele nu ar trebui să spargă construirea. Testele ar trebui să fie un indiciu că aplicația nu are noi bug-uri introduse pentru funcționalitatea testată (și o eroare găsită este o indicație a unui test lipsă).

Dacă testele nu întrerup construirea, atunci puteți rula cu ușurință în situația în care noul cod rupe construirea și nu este cunoscut pentru un timp, chiar dacă un test le-a acoperit. Un test de eșec ar trebui să fie un steguleț roșu care trebuie să fie stabilit fie pentru test, fie pentru cod.

În plus, permițând testelor să nu spargă construirea, va cauza rata de defecțiune să încetinească încet până la punctul în care nu mai aveți un set fiabil de teste de regresie.

Dacă există o problemă cu ruperea prea tare a testelor, poate fi un indiciu că testele sunt scrise într-un mod prea fragil (dependența de resurse care s-ar putea schimba, cum ar fi baza de date fără a utiliza DB Unitatea în mod corespunzător sau un serviciu web extern care ar trebui să fie batjocorit), sau poate fi un indiciu că există dezvoltatori în echipă care nu dau atenția corectă.

Cred cu tărie că un test de eșec ar trebui să fie reprimat ASAP, așa cum ați repara codul care nu reușește să compileze ASAP.

0
adăugat

Nu sunt sigur de ce ați spune că testele de unitate vor fi eliminate odată ce refactorizarea este finalizată. De fapt, suita dvs. de test-unitate ar trebui să ruleze după construirea principală (puteți crea o construcție separată "teste", care rulează doar testele unității după construirea principalului produs). Apoi, veți vedea imediat dacă modificările dintr-o singură piesă întrerup testele din alt subsistem. Rețineți că este puțin diferit de executarea testelor în timpul construcției (cum ar putea să se susțină unii) - unele teste limitate sunt utile în timpul construirii, dar de obicei este neproductiv să "prăbușească" construirea doar pentru că un test de unitate se întâmplă să eșueze.

Dacă scrieți Java (sunt șanse), verificați http://www.easymock.org/ - poate fi utilă pentru reducerea cuplării în scopuri de testare.

0
adăugat

Am citit Working Effective With Legacy Code și sunt de acord că este foarte util pentru a trata codul "netestabil".

Unele tehnici se aplică numai limbilor compilate (lucrez la "vechile" aplicații PHP), dar aș spune că cea mai mare parte a cărții este aplicabilă oricărei limbi.

Cărțile de refactorizare presupun uneori că codul este în stare semi-ideală sau "în stare de întreținere" înainte de refactorizare, dar sistemele pe care le lucrez sunt mai puțin decât ideale și au fost dezvoltate ca aplicații "învățați când mergeți" sau ca primele aplicații pentru unele tehnologii folosite (și nu învinovățesc dezvoltatorii inițiali pentru asta, deoarece eu sunt unul dintre ei), deci nu există nici un test, și codul este uneori dezordonat. Această carte se adresează acestui tip de situație, în timp ce alte cărți de refactorizări nu sunt de obicei (bine, nu în această măsură).

Trebuie să menționez că nu am primit niciun fel de bani din partea editorului și autorului acestei cărți; dar mi sa părut foarte interesant, deoarece lipsesc resursele în domeniul codului moștenit (și în special în limba mea, franceză, dar asta e alta poveste).

0
adăugat