Hoe eenheidstests voor database-oproepen schrijven

Ik ben in de buurt van het begin van een nieuw project en (HAPP!) Voor de eerste keer probeer ik eenheidstests op te nemen in een project van mij.

Ik heb problemen met het bedenken van een aantal van de eenheidstests zelf. Ik heb een paar methoden die gemakkelijk genoeg zijn geweest om te testen (passeer in twee waarden en controleer op een verwachte output). Ik heb andere delen van de code die meer complexe dingen doen zoals het uitvoeren van query’s tegen de database en ik weet niet zeker hoe ik ze moet testen.

public DataTable ExecuteQuery(SqlConnection ActiveConnection, string Query, SqlParameterCollection Parameters)
{
    DataTable resultSet = new DataTable();
    SqlCommand queryCommand = new SqlCommand();
    try
    {
        queryCommand.Connection = ActiveConnection;
        queryCommand.CommandText = Query;
        if (Parameters != null)
        {
            foreach (SqlParameter param in Parameters)
            {
                 queryCommand.Parameters.Add(param);
            }
        }
        SqlDataAdapter queryDA = new SqlDataAdapter(queryCommand);
        queryDA.Fill(resultSet);
    }
    catch (Exception ex)
    {
        //TODO: Improve error handling
        Console.WriteLine(ex.Message);
    }
    return resultSet;
}

Deze methode neemt in wezen alle noodzakelijke bits en stukken in om sommige gegevens uit de database te halen en de gegevens in een datumable-object retourneren.

De eerste vraag is waarschijnlijk het meest complex: wat moet ik zelfs in een dergelijke situatie testen?

Eenmaal dat is afgewikkeld, komt de vraag of u de databasecomponenten of niet wilt bespotten of probeert te testen tegen de werkelijke DB.


Antwoord 1, Autoriteit 100%

Wat test u?

Er zijn drie mogelijkheden, van de bovenkant van mijn hoofd:

a. U testen van de DAO-klasse (Gegevens Access Object), zorgt ervoor dat het correct marshaling is, de waarden / parameters die worden doorgegeven aan de database ,, en correct Marshaling / Transforming / Packaging Resultaten Gotten FRM de database.

In dit geval hoeft u helemaal geen verbinding te maken met de database; U hebt alleen een eenheidstest nodig die de database (of tussenlaag, bijv., JDBC, (n) hibernate, Ibatis) vervangt met een mock.

b. U test de syntactische juistheid van (gegenereerde) SQL.

In dit geval, omdat SQL-dialecten verschillen, wilt u de (mogelijk gegenereerde) SQL uitvoeren tegen de juiste versie van uw RDBMS, in plaats van te proberen alle eigenaardigheden van uw RDBMS te bespotten (en zodat alle RDBMS-upgrades die de functionaliteit wijzigen worden gepakt door uw tests).

c. U test het Semantic correctheid van uw SQL, I.E, dat voor een gegeven baseline-dataset, uw operaties (toegangen / selecties en mutaties / inzetstukken en updates) produceren de verwachte nieuwe dataset.

Daarvoor wilt u iets als dbunit gebruiken (waarmee u een basislijn kunt instellen en een resultaat kunt vergelijken met een verwachte resultaat instellen), of mogelijk uw tests geheel wilt in de database, met behulp van de techniek die ik hier een overzicht kan maken : beste manier om SQL-query’s te testen .


Antwoord 2, Autoriteit 76%

Dit is de reden waarom (IMHO) eenheidstests soms een vals gevoel van veiligheid creëren van het deel van de ontwikkelaars. In mijn ervaring met toepassingen die met een database praten, zijn fouten gewoonlijk het gevolg van gegevens die in een onverwachte of ontbrekende waarden etc.) zijn. Als u de gegevenstoegang van uw eenheid routinematig opdikt in uw eenheidstests, denkt u dat uw code goed werkt wanneer het inderdaad nog steeds kwetsbaar is voor dit soort fout.

Ik denk dat uw beste aanpak is om een ​​testdatabase handig te hebben, gevuld met schelsten van waardeloze gegevens, en uw database-componenttests tegen dat kan uitvoeren. Al het moment onthouden dat uw gebruikers veel beter zullen zijn dan u uw gegevens bevindt.


Antwoord 3, Autoriteit 30%

Het hele punt van een eenheidstest is om een eenheid te testen(duh) geïsoleerd. Het hele punt van een database-aanroep is om te integrerenmet een andere eenheid (de database). Ergo: het heeft geen zin om database-aanroepen te unit-testen.

U moet echter database-aanroepen voor integratietests doen (en u kunt desgewenst dezelfde tools gebruiken die u gebruikt voor unit-testing).


Antwoord 4, autoriteit 20%

Voor de liefde van God, test niet met een live, reeds bevolkte database. Maar dat wist je.

Over het algemeen heb je al een idee van wat voor soort gegevens elke zoekopdracht gaat ophalen, of je nu gebruikers authenticeert, telefoonboek-/organigram-items opzoekt, of wat dan ook. U weet in welke velden u geïnteresseerd bent en u weet welke beperkingen daarop bestaan ​​(bijv. UNIQUE, NOT NULL, enzovoort). U bent een eenheid die uw code test die interageert met de database, niet de database zelf, dus denk in termen van hoe u die functies kunt testen. Als het mogelijk is dat een veld NULLis, moet u een test hebben die ervoor zorgt dat uw code NULL-waarden correct verwerkt. Als een van je velden een tekenreeks is (CHAR, VARCHAR, TEXT, &c), test dan of je met escaped omgaat tekens correct.

Veronderstel dat gebruikers zullen proberen om alles* in de database te plaatsen en dienovereenkomstig testgevallen te genereren. U wilt hiervoor nepobjecten gebruiken.

* Inclusief ongewenste, kwaadaardige of ongeldige invoer.


Antwoord 5, autoriteit 10%

Strikt genomen is een test die schrijft/leest uit een database of een bestandssysteem geen unit-test. (Hoewel het een integratietest kan zijn en geschreven kan zijn met NUnit of JUnit). Unit-tests worden verondersteld operaties van een enkele klasse te testen, waarbij de afhankelijkheden worden geïsoleerd. Dus als je unit-test schrijft voor de interface en de bedrijfslogica-lagen, zou je helemaal geen database nodig moeten hebben.

Ok, maar hoe test je de databasetoegangslaag als eenheid? Ik hou van het advies uit dit boek: xUnit Test Patterns(de link verwijst naar de ” Testen met DB” hoofdstuk. De toetsen zijn:

  • retourtesten gebruiken
  • schrijf niet te veel tests in uw gegevenstoegangstestprogramma, omdat ze veel langzamer zullen werken dan uw “echte” eenheidstests
  • als je testen met een echte database kunt vermijden, test dan zonder database

Antwoord 6, autoriteit 8%

Je kunt alles testen, behalve: queryDA.Fill(resultSet);

Zodra u queryDA.Fill(resultSet)uitvoert, moet u ofwel de database imiteren/faken, of u doet integratietests.

Ten eerste zie ik integratietesten niet als slecht, het is alleen dat het een ander soort bug opvangt, verschillende kansen heeft op valse negatieven en valse positieven, en dat het waarschijnlijk niet vaak zal worden gedaan omdat het is zo traag.

Als ik deze code zou testen, zou ik valideren dat de parameters correct zijn gebouwd, maakt de opdrachtbouwer het juiste aantal parameters? Hebben ze allemaal een waarde? Worden nulls, lege strings en DbNull correct verwerkt?

Het vullen van de dataset is eigenlijk het testen van uw database, wat een schilferig onderdeel is dat buiten het bereik van uw DAL valt.


Antwoord 7, autoriteit 4%

Voor unit-tests bespot of vervals ik meestal de database. Gebruik vervolgens uw nep- of nep-implementatie via afhankelijkheidsinjectie om uw methode te testen. U hebt waarschijnlijk ook enkele integratietests die beperkingen, externe-sleutelrelaties, enz. in uw database testen.

Voor wat u zou testen, zou u ervoor moeten zorgen dat de methode de verbinding van de parameters gebruikt, dat de queryreeks is toegewezen aan de opdracht en dat de geretourneerde resultatenset dezelfde is als die u opgeeft via een verwachting op de Fill-methode. Opmerking — het is waarschijnlijk gemakkelijker om een ​​Get-methode te testen die een waarde retourneert dan een Fill-methode waarmee een parameter wordt gewijzigd.


Antwoord 8, autoriteit 4%

De eerste vraag is waarschijnlijk de meest complexe: wat moet ik zelfs testen in een situatie als deze?

  • Aangezien uw codecode in feite een DAO/repository is zonder enige
    bedrijfslogica je hebt een integratietest nodig, geeneen unittest.

  • Eenheidstest zou klassen moeten testen zonder externe afhankelijkheden (zoals DB
    of oproepen naar andere externe services).

  • U moet altijd proberen de bedrijfslogica (uw domein
    Model) code van infrastructuurcode, dan is het een gemakkelijk te gebruiken eenheid
    testen.

  • Wees voorzichtig met Mocks, het kan een signaal zijn van een slecht ontwerp. Het betekent
    uw bedrijfslogica is vermengd met infrastructuur.

  • Controleer deze patronen: “Domeinmodel”, “Zeshoekige architectuur”, “Functionele kern, imperatieve schaal”


Antwoord 9, autoriteit 2%

Om dit op de juiste manier te doen, zou je een afhankelijkheidsinjectie (DI) moeten gebruiken, en voor .NET zijn er verschillende. Ik gebruik momenteel het Unity Framework, maar er zijn andere die eenvoudiger zijn.

Hier is één link van deze site over dit onderwerp, maar er zijn andere:
Dependency Injection in .NET met voorbeelden?

Hierdoor kunt u gemakkelijker andere delen van uw toepassing nabootsen, door gewoon een proefklasse de interface te laten implementeren, zodat u kunt bepalen hoe deze zal reageren. Maar dit betekent ook ontwerpen naar een interface.

Aangezien je naar de beste werkwijzen vroeg, zou dit er een zijn, IMO.

Ga dan niet naar de database tenzij het echt nodig is, zoals gesuggereerd is een andere.

Als u bepaald gedrag moet testen, zoals externe sleutelrelaties met trapsgewijs verwijderen, dan wilt u daar misschien databasetests voor schrijven, maar over het algemeen is het het beste om niet naar een echte database te gaan, vooral omdat meer dan één persoon een eenheidstest tegelijk en als ze naar dezelfde database gaan, kunnen de tests mislukken omdat de verwachte gegevens kunnen veranderen.

Bewerken: Met database unit-test bedoel ik dit, omdat het is ontworpen om alleen t-sql te gebruiken om wat setup, testen en demontage uit te voeren.
http://msdn.microsoft.com/en- us/library/aa833233%28VS.80%29.aspx


Antwoord 10

Bij op JDBC gebaseerde projecten kan de JDBC-verbinding worden bespot, zodat tests kunnen worden uitgevoerd zonder live RDBMS, waarbij elke testcase wordt geïsoleerd (geen gegevensconflict).

Hiermee kan worden geverifieerd dat de persistentiecode de juiste query’s/parameters doorgeeft (bijv. https://github.com/playframework/playframework/blob/master/framework/src/anorm/src/test/scala/anorm/ParameterSpec.scala) en behandel JDBC-resultaten (parsing/mapping) zoals verwacht (“neemt alle benodigde stukjes en beetjes op om enkele gegevens uit de database te extraheren en geeft de gegevens terug in een DataTable-object”).

Framework zoals jOOQ of mijn framework Acolyte kan worden gebruikt voor: https://github.com/cchantep/acolyte .

Other episodes