Cum ar trebui să test unitatea un generator de cod?

Aceasta este o întrebare dificilă și deschisă pe care o cunosc, dar m-am gândit să o arunc pe podea și să văd dacă cineva a avut sugestii interesante.

Am dezvoltat un generator de coduri care ia interfața noastră Python cu codul nostru C ++ (generat prin SWIG) și generează codul necesar pentru a expune acest lucru ca WebServices. Când am dezvoltat acest cod, am făcut-o folosind TDD, dar mi-am găsit testele să fie fragile ca naiba. Deoarece fiecare test a vrut în esență să verifice că pentru un anumit bit de cod de intrare (care se întâmplă să fie un antet C ++) aș obține un bit dat de codul de ieșire am scris un mic motor care citește definițiile de test din fișierele de intrare xml și generează testul cazuri din aceste așteptări.

Problema este că mă deranjez să intru în modificarea codului. Acest lucru și faptul că unitățile se testează sunt: ​​complexe și b: fragile.

Așadar, încerc să mă gândesc la abordări alternative la această problemă și mi se pare că probabil că o abordez într-un mod greșit. Poate că trebuie să mă concentrez mai mult asupra rezultatului, IE: genul pe care l-am generat de fapt funcționează și fac ceea ce vreau, mai degrabă decât codul arată așa cum vreau eu.

A avut cineva experiențe de ceva asemănător cu care ar fi interesat să împărtășească?

0
fr hi bn
@James: nu există un răspuns ușor acolo ... Am re-citit această întrebare și răspunsurile și toate problemele pe care le aveam la acel moment vin înapoi înapoi. S-ar putea să dau o altă lovitură în următoarele săptămâni, pentru că mă sfârșesc cu diferite regresii din timp în timp și devine din ce în ce mai critic pentru a le detecta.
adăugat autor jkp, sursa
Este o comparație masivă de șir masiv. Ar putea fi mai ușor folosind un AST
adăugat autor Nikos, sursa
De fapt, mă confrunt cu aceeași problemă și niciunul dintre răspunsurile de mai jos nu este într-adevăr satisfăcător. Acordat, puteți verifica piesele unui generator de coduri. Problema este cum știți că codul generat este corect, adică că nu există regresii sau ceva asemănător și, prin urmare, cum scrieți teste automate pentru codul generat (indiferent dacă acestea se numesc teste unitare sau de integrare)?
adăugat autor James Kingsbery, sursa

8 răspunsuri

Dacă rulați pe * nux s-ar putea să luați în considerare dumpingul cadrului unității în favoarea unui script bash sau a unui makefile. pe ferestre, puteți lua în considerare construirea unei aplicații/funcții shell care să ruleze generatorul și apoi să utilizeze codul (ca un alt proces) și unitatea să încerce acest lucru.

O a treia opțiune ar fi să genereze codul și apoi să construiască o aplicație care să nu conțină decât un test unit. Din nou, ai avea nevoie de un script de shell sau de ce să nu executați acest lucru pentru fiecare intrare. În ceea ce privește modul de codare a comportamentului așteptat, mi se pare că ar putea fi făcut în același mod ca și pentru codul C ++ folosind doar interfața generată mai degrabă decât cea C ++.

0
adăugat

Am început să scriu un rezumat al experienței mele cu propriul meu generator de coduri, apoi m-am întors și mi-am re-citit întrebarea și am constatat că deja ai atins aceleași probleme și că te concentrezi pe rezultatele execuției în loc de aspectul/aspectul codului.

Problema este că este greu de testat, că codul generat ar putea să nu fie potrivit pentru a funcționa efectiv în mediul sistemului de testare a unităților și cum codificați rezultatele așteptate?

Am descoperit că trebuie să defalcați generatorul de cod în bucăți mai mici și să le testați pe unitate. Unitatea de testare a unui generator de cod completat este mai mult ca o testare de integrare decât unitatea de testare dacă mă întrebi.

0
adăugat
Da, cum ar fi compararea unui șir masiv cu altul, care trebuie să se ocupe de chestii precum: Valoarea estimată egală: "Opțiuni": dar a primit {"opțiuni":
adăugat autor Nikos, sursa

Recall that "unit testing" is only one kind of testing. You should be able to unit test the internal pieces of your code generator. What you're really looking at here is system level testing (a.k.a. regression testing). It's not just semantics... there are different mindsets, approaches, expectations, etc. It's certainly more work, but you probably need to bite the bullet and set up an end-to-end regression test suite: fixed C++ files -> SWIG interfaces -> python modules -> known output. You really want to check the known input (fixed C++ code) against expected output (what comes out of the final Python program). Checking the code generator results directly would be like diffing object files...

0
adăugat

Da, rezultatele sunt singurul lucru care contează. Lucru real este scrierea unui cadru care să permită ca codul dvs. generat să funcționeze independent ... petreceți timpul acolo.

0
adăugat

Recomandarea mea ar fi să îmi dau seama de un set de rezultate cunoscute de ieșire și de ieșire, cum ar fi unele cazuri mai simple pe care le-ați instalat deja, și unitatea de testare a codului produs . Este foarte posibil ca, pe măsură ce schimbați generatorul, șirul exact care este produs poate fi ușor diferit ... dar ceea ce vă interesează cu adevărat este dacă este interpretat în același mod. Astfel, dacă testați rezultatele pe măsură ce testați codul dacă acesta ar fi caracteristica dvs., veți afla dacă acesta reușește în modul în care doriți.

Practic, ceea ce vrei cu adevărat să știi este dacă generatorul tău va produce ceea ce te aștepți fără a testa fizic orice combinație posibilă (de asemenea: imposibilă). Asigurându-vă că generatorul dvs. este compatibil în modul în care vă așteptați, vă puteți simți mai bine că generatorul va reuși în situații din ce în ce mai complexe.

În acest fel, puteți crea o serie de teste de regresie (teste unitare care trebuie să funcționeze corect). Acest lucru vă va ajuta să vă asigurați că modificările aduse generatorului dvs. nu încalcă alte forme de cod. Când întâmpinați un bug pe care testele unității dvs. nu l-au prins, poate doriți să îl includeți pentru a preveni ruperea similară.

0
adăugat

Doar am vrut să subliniez faptul că puteți obține în continuare teste cu granulație fină în timp ce verificați rezultatele: puteți testa bucăți individuale de cod prin asamblarea lor în interiorul unor coduri de configurare și de verificare:

int x = 0;
GENERATED_CODE
assert(x == 100);

Cu condiția să aveți codul dvs. generat asamblat din bucăți mai mici și bucățile nu se schimbă frecvent, puteți exercita mai multe condiții și puteți testa un pic mai bine și sperăm să evitați să vă întrerupeți toate încercările când schimbați specificul unei bucăți.

0
adăugat

Testul unității este doar testarea unei unități specifice. Deci, dacă scrieți o specificație pentru clasa A, este ideal dacă clasa A nu are versiunile reale concrete ale clasei B și C.

Ok, am observat că tag-ul pentru această întrebare include C ++/Python, dar principiile sunt aceleași:

    public class A : InterfaceA 
    {   
      InterfaceB b;

      InterfaceC c;

      public A(InterfaceB b, InterfaceC c)   {
          this._b = b;
          this._c = c;   }

      public string SomeOperation(string input)   
      {
          return this._b.SomeOtherOperation(input) 
               + this._c.EvenAnotherOperation(input); 
      } 
    }

Deoarece sistemul A de mai sus injecta interfețe la sistemele B și C, puteți testa unitatea doar sistemul A, fără ca funcționalitatea reală să fie executată de orice alt sistem. Acesta este testul unitar.

Iată o modalitate inteligentă pentru abordarea unui sistem de la creație până la finalizare, cu o altă specificație Când se specifică pentru fiecare piesă de comportament:

public class When_system_A_has_some_operation_called_with_valid_input : SystemASpecification
{
    private string _actualString;

    private string _expectedString;

    private string _input;

    private string _returnB;

    private string _returnC;

    [It]
    public void Should_return_the_expected_string()
    {
        _actualString.Should().Be.EqualTo(this._expectedString);
    }

    public override void GivenThat()
    {
        var randomGenerator = new RandomGenerator();
        this._input = randomGenerator.Generate();
        this._returnB = randomGenerator.Generate();
        this._returnC = randomGenerator.Generate();

        Dep().Stub(b => b.SomeOtherOperation(_input))
                         .Return(this._returnB);
        Dep().Stub(c => c.EvenAnotherOperation(_input))
                         .Return(this._returnC);

        this._expectedString = this._returnB + this._returnC;
    }

    public override void WhenIRun()
    {
        this._actualString = Sut.SomeOperation(this._input);
    }
}

Așadar, în concluzie, o singură unitate/specificație poate avea mai multe comportamente, iar specificația crește atunci când dezvolți unitatea/sistemul; iar dacă sistemul dvs. este testat depinde de alte sisteme concrete din interiorul acestuia, aveți grijă.

0
adăugat

Consider că trebuie să testați ceea ce generați mai mult decât cum îl generați.

În cazul meu, programul generează multe tipuri de cod (C #, HTML, SCSS, JS, etc.) care se compilesc într-o aplicație web. Cea mai bună metodă pe care am descoperit-o pentru a reduce erorile de regresie în general este de a testa aplicația web în sine, nu prin testarea generatorului.

Nu mă înțelegeți greșit, există încă teste unitare care verifică unele din codul generator, dar cea mai mare lovitură pentru dolarul nostru a fost testele UI pe aplicația generată în sine.

Since we're generating it we also generate a nice abstraction in JS we can use to programatically test the app. We followed some ideas outlined here: http://code.tutsplus.com/articles/maintainable-automated-ui-tests--net-35089

Partea mare este că testează cu adevărat sistemul dvs. de la sfârșitul la sfârșit, de la generarea de coduri la ceea ce generați de fapt. Odată ce un test nu reușește, este ușor să-l urmăriți înapoi spre locul în care a rupt generatorul.

E drăguț.

Mult noroc!

0
adăugat
Python România
Python România
100 participanți

Comunitatea pasionaților de Python din România.