Check alle échte Black Friday-deals Ook zo moe van nepaanbiedingen? Wij laten alleen échte deals zien

Hoe eigen algoritmes testen?

Pagina: 1
Acties:

  • Daos
  • Registratie: Oktober 2004
  • Niet online
Ik ben steeds meer fan van Unit-tests (ik doe alleen meestal niet aan testen maken voor code schrijven). Nu loop ik tegen het volgende aan: met Unit-tests test je alleen de werking voor je API/public-methoden.

Nu wil ik soms ook mijn eigen algoritmes in kleinere stapjes testen en zoek ik naar de beste methode hiervoor.

Neem bijvoorbeeld (berekent een absoluut verschil in kortste afstand vanaf de oorsprong waarbij je alleen links/rechts en beneden/boven mag gaan)
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
Tuple<int, int> item1 = new Tuple<int, int>(2, 5);
Tuple<int, int> item2 = new Tuple<int, int>(7, 4);

int res = MyAlgorithm(item1, item2);

public int MyAlgorithm(Tuple<int, int> item1, Tuple<int, int> item2)
{
    // define constants
    int[] min = { -5, -10 };
    int[] max = { 20, 10 };

    // input checks
    Debug.Assert(min[0] <= item1.Item1 && item1.Item1 <= max[0]);
    Debug.Assert(min[0] <= item2.Item1 && item2.Item1 <= max[0]);
    Debug.Assert(min[1] <= item1.Item2 && item1.Item2 <= max[1]);
    Debug.Assert(min[1] <= item2.Item2 && item2.Item2 <= max[1]);

    // create field
    int[,] field = new int[max[0] - min[0] + 1, max[1] - min[1] + 1];

    // init
    for (int i = 0; i < field.GetLength(0); i++)
    {
        for (int j = 0; j < field.GetLength(1); j++)
        {
            if (i > 0 && j == 0)
                field[i, j] = field[i - 1, j] + 1;
            else if (j > 0)
                field[i, j] = field[i, j - 1] + 1;
        }
    }

    // processing
    int val1 = field[item1.Item1, item1.Item2];
    int val2 = field[item2.Item1, item2.Item2];

    int res = Math.Abs(val1 - val2);

    // return
    return res;
}


Ik wil hierin elk 'blokje' tussen regels commentaar testen.

De oplossingen die ik bedacht heb (en eventueel waarom ik die niet wil):
1 De code opsplitsen in losse functies en die public maken zodat je ze kan testen.
Mijn bezwaren:
- ik wil niet dat mijn onderdelen los gebruikt kunnen worden door anderen
- mijn algoritme is mentaal moeilijker te volgen

2 Via Aspect Oriented Programming (AOP) de data bewerken voor (zo wordt soms ook Fault-injection gedaan) en bekijken na een blokje code.


Via Aspect Oriented Programming lijkt mij wel een goed idee; zeker als ik dit stop in een Unit-test. Ik kan alleen amper wat over vinden over testen via AOP en ook de frameworks voor AOP zelf zijn schaars (zeker voor C#).

De vraag is dan ook: hoe testen jullie de interne werking van eigen algoritmes? (en als je het via AOP doet: met welk framework en waarom die?)

offtopic:
Om het topic ontopic te houden:
De gegeven code is een voorbeeld speciaal voor dit topic. Ik weet dat het met andere algoritmes beter kan. Wijs mij daarom niet op andere algoritmes. Het gaat om het testen van dit of soortgelijke algoritmes (op bepaalde manier opsplitsen oid is wel ontopic)

Ik weet dat sommige mensen het testen van implementatie afkeuren. Overtuig mij niet dat ik dat niet moet doen. Soms kom je situaties tegen zoals in dit voorbeeld waarin je het juist wel wil doen.

  • Ulster Seedling
  • Registratie: December 2007
  • Laatst online: 14:24

Ulster Seedling

“Middelgrote appel”

Ik weet niet of er een methode bestaat die precies aansluit bij je vraag. Eerlijk gezegd ben ik ook niet heel ervaren in verschillende testmethoden, dus hopelijk kan iemand anders hier nog iets over zeggen.

Wat misschien wel kan is de correctheid van je algoritme bewijzen en vervolgens je implementatie hierop laten lijken. Daarvoor zou je je te bewijzen algoritme wellicht wat aan moeten passen zodat het goed aansluit bij de implementatie die je gemaakt hebt. Ik weet niet of dit een echt bewijs zou zijn, maar het lijkt me wel een manier om te laten zien dat je implementatie waarschijnlijk goed is.

(Bijvoorbeeld: als in je algoritme x ← min(a, b) en in je implementatie x = Math.min(a, b); staat, en je algoritme is bewezen correct, dan ben je m.i. een aardig eind op weg.)

“(…) met een rode blos op een geelgroene ondergrond.” Volgens Wikipedia tenminste.


  • Diablow
  • Registratie: Januari 2001
  • Laatst online: 04-04 18:56
Ik denk dat je er dan gewoon meerdere methodes van moet maken, is ook beter...

  • Daos
  • Registratie: Oktober 2004
  • Niet online
Ulster Seedling schreef op zaterdag 12 april 2014 @ 15:03:
Wat misschien wel kan is de correctheid van je algoritme bewijzen en vervolgens je implementatie hierop laten lijken.
Dit lijkt mij niet erg praktisch. Bewijzen zelf schijnt vaak niet mogelijk te zijn en met de hand implementeren/vergelijken is weer gevoelig voor fouten (je hebt dan weer tests nodig om die te vinden).

Automatisch bewijzen zie je soms ook, maar dan weer niet voor normale programmeertalen. Ik ga me er wel weer eens in verdiepen. Er lijkt wat vooruitgang te zijn geboekt de laatste jaren.

Het gaat trouwens niet om het kunnen laten zien dat het goed is, maar dat het zo goed als goed is ;)
Diablow schreef op zaterdag 12 april 2014 @ 16:09:
Ik denk dat je er dan gewoon meerdere methodes van moet maken,
Dat was optie 1 waar ik aan dacht.
is ook beter...
Bedankt voor je constructieve bijdrage aan dit topic }:|

  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
Je topictitel rept over "eigen algoritmes" maar je doelt gewoon op privates / internals. En die kun je unittesten door je assembly te voorzien van een InternalsVisibleTo attribute. In oudere visual studio versies (pin me d'r niet op vast, maar ik meen tot 2010) was dat wat handiger aan de praat te krijgen dan in latere versies (2012, 2013) maar het moet nog steeds kunnen. Dat is wél een stuk eenduidiger dan gaan lopen frotten met AOP en alles wat daar weer bij komt kijken (overigens is er genoeg over AOP (ook voor C# specifiek) te vinden; ik weet niet hoe jij zoekt, maar één van de eerste resultaten is Postsharp, een alternatief is het Policy Injection Application Block uit de Enterprise library en hier staat nog een lijstje met meer alternatieven). AOP heeft weer z'n eigen nukken (vaak eerst compilen waarna postsharp (of een ander product/project) weer een extra slag maakt om in de gegenereerde MSIL weer z'n eigen meuk te injecten (weaving genaamd) etc. etc... not a big fan...) en of 't dé oplossing is voor je vraag betwijfel ik.

Verder staat hier meer dan genoeg interessants en alternatieven (zoals reflection gebruiken), pro's en cons voor 't testen van internals etc. Lees dat eens door voor wat inspiratie voor de opties die je hebt en of je eigenlijk wel internals wil testen (waar soms wel iets voor te zeggen is hoor); het is dan misschien niet c# specifiek maar net zo goed van toepassing.

[ Voor 40% gewijzigd door RobIII op 12-04-2014 17:23 ]

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


  • Daos
  • Registratie: Oktober 2004
  • Niet online
RobIII schreef op zaterdag 12 april 2014 @ 17:07:
Je topictitel rept over "eigen algoritmes" maar je doelt gewoon op privates / internals.
Het gaat ook om stukjes binnen functies. Ik maak wel vaak gebruik van andere wat algemenere functies die nodig zijn in het algoritme, maar dingen die specifiek voor het algoritme zijn (= samen het algoritme) stop ik het liefst in 1 functie zodat ik het algoritme kan volgen en begrijpen.
En die kun je unittesten door je assembly te voorzien van een InternalsVisibleTo attribute. In oudere visual studio versies (pin me d'r niet op vast, maar ik meen tot 2010) was dat wat handiger aan de praat te krijgen dan in latere versies (2012, 2013) maar het moet nog steeds kunnen.
Ik wist niet dat dit bestond. Ik moet dan wel mijn algoritme gaan opsplitsen (tegenwoordig wel te doen voor me: vroeger maakte ik reuzen-loops; nu meerdere aparte na elkaar).

Ook dat dit alleen met internal en niet met private werkt zit me niet erg lekker. Ik prop meestal alles in 1 namespaceassembly en kan dan net zo goed alle functies public maken.
Dat is wél een stuk eenduidiger dan gaan lopen frotten met AOP en alles wat daar weer bij komt kijken (overigens is er genoeg over AOP (ook voor C# specifiek) te vinden; ik weet niet hoe jij zoekt, maar één van de eerste resultaten is Postsharp,
[...]
AOP heeft weer z'n eigen nukken (vaak eerst compilen waarna postsharp (of een ander product/project) weer een extra slag maakt om in de gegenereerde MSIL weer z'n eigen meuk te injecten (weaving genaamd) etc. etc... not a big fan...) en of 't dé oplossing is voor je vraag betwijfel ik.
Ik was positief over AOP omdat ik dan mijn code niet hoef aan te passen en wel goed kan testen (hoop ik).
Verder staat hier meer dan genoeg interessants en alternatieven (zoals reflection gebruiken), pro's en cons voor 't testen van internals etc. Lees dat eens door voor wat inspiratie voor de opties die je hebt en of je eigenlijk wel internals wil testen (waar soms wel iets voor te zeggen is hoor); het is dan misschien niet c# specifiek maar net zo goed van toepassing.
Dank je. Ik zal het doorlezen.

[ Voor 0% gewijzigd door Daos op 12-04-2014 18:04 . Reden: internal is voor assemblies ]


  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
Daos schreef op zaterdag 12 april 2014 @ 17:50:
het liefst in 1 functie zodat ik het algoritme kan volgen en begrijpen.
Raar... als je je mentale overhead laag wil houden (lees: beter kunnen volgen/begrijpen) dan is het juist beter wat extra private methods te introduceren (met een fatsoenlijke naamgeving).

Zeg nou zelf; wat leest er makkelijker?
code:
1
2
3
if (date>=new date(2014,4,8) && (datetime.now-popup.lastdisplayed).totalSeconds >= 3600 && !popup.visible) {
  //display XP is no longer supported popup
}


code:
1
2
3
if ShouldDisplayNoLongerSupported(popup) {
  //display XP is no longer supported popup
}
Daos schreef op zaterdag 12 april 2014 @ 17:50:
Ik moet dan wel mijn algoritme gaan opsplitsen.
Je zult je methods moeten opsplitsen als je afzonderlijke delen wil testen; "algoritme" staat hier los van. Een algoritme kun je (en zul je) vaak ook in meerdere methods stoppen.
Daos schreef op zaterdag 12 april 2014 @ 17:50:
vroeger maakte ik reuzen-loops; nu meerdere aparte na elkaar
Ik zie echt niet hoe dit relevant is; deze hele zin slaat, in de context van dit topic, redelijk als een tang op een varken. Of je nou een boel doet in 1 loop of in 20 loops achter elkaar; er is afhankelijk van de context en wat je code doet géén of juist een wezenlijk verschil. Maar hoe dan ook weinig relevant in 't daglicht van unittesten. Het heet overigens niet voor niets unittesten; je test units. De kleinste unit is een method (of iets breder gezegd: een class member); "daarbinnen" valt er niets te unittesten (althans: dat zou 't niet moeten zijn; is het dat wél dan is dat je cue dat je er aparte private methods van had moeten maken in the first place).
Daos schreef op zaterdag 12 april 2014 @ 17:50:
Ik prop meestal alles in 1 namespace en kan dan net zo goed alle functies public maken.
Namespaces zijn er niet voor niets, (al dan niet private) methods, properties, alles heeft z'n nut en doel; als jij dat vervolgens overboord kiepert omdat jij graag anders werkt moet je niet raar opkijken als "het systeem" niet lekker met je meewerkt ;) Ik begrijp overigens (denk ik wel) wat je bedoelt hiermee, maar ook namespaces hebben weer weinig van doen met public methods; het enige dat ze gemeen hebben is de classes en die bepalen op hun beurt weer hoe "public" of niet bepaalde code is ;)

Tot slot:
Part­ing thoughts…
Before you jump in with both feet and start test­ing all the private/internal meth­ods, there’s a cou­ple of ques­tions you should always ask your­self first:
  1. The most impor­tant ques­tion of them all is whether or not these meth­ods are even worth testing!
  2. If they are worth test­ing, could they be moved into a sep­a­rate class?
  3. Could you cover all the test cases by mak­ing more calls to the pub­lic meth­ods that call the pri­vate methods?

[ Voor 105% gewijzigd door RobIII op 12-04-2014 18:20 ]

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


  • Daos
  • Registratie: Oktober 2004
  • Niet online
RobIII schreef op zaterdag 12 april 2014 @ 17:53:
Raar... als je je mentale overhead laag wil houden (lees: beter kunnen volgen/begrijpen) dan is het juist beter wat extra private methods te introduceren (met een fatsoenlijke naamgeving).

Zeg nou zelf; wat leest er makkelijker?
code:
1
2
3
if (date > new date(2014,4,8) && (date.now - popup.lastdisplayedtime).totalSeconds >= 3600 && !popup.visible) then
  //display XP is no longer supported popup
/if


code:
1
2
3
if  ShouldDisplayNoLongerSupported(popup) then
  //display XP is no longer supported popup
/if
Ik zet meestal commentaar boven de stukjes code. Het wordt dan zoiets:
code:
1
2
3
4
5
6
7
8
9
// determine whether to show not supported popup
supportExpired = date > new date(2014,4,8)
warnedLately = (date.now - popup.lastdisplayedtime).totalSeconds < 3600
shouldDisplayNoLongerSupported = supportExpired && !warnedLately && !popup.visible

// popup if necessary
if (shouldDisplayNoLongerSupported) then
  //display XP is no longer supported popup
/if
Ik zie echt niet hoe dit relevant is; deze hele zin slaat, in de context van dit topic, redelijk als een tang op een varken. Of je nou een boel doet in 1 loop of in 20 loops achter elkaar; er is afhankelijk van de context en wat je code doet géén of juist een wezenlijk verschil.
Mijn code was van dusdanige kwaliteit dat het vrijwel niet op te splitsen was in aparte functies.
Namespaces zijn er niet voor niets, (al dan niet private) methods, properties, alles heeft z'n nut en doel; als jij dat vervolgens overboord kiepert omdat jij graag anders werkt moet je niet raar opkijken als "het systeem" niet lekker met je meewerkt ;)
Het valt wel mee met hoeveel ik overboord gooi. Ik bouw geen enterprise-applicaties van miljoenen regels code. Soms zou ik misschien 2 of 3 namespaces kunnen gebruiken.

Properties gebruik ik voor het bewaren van de toestand van een object voor de buitenwereld. Intern voor de communicatie tussen methods/functies gebruik ik argumenten/parameters en return values. Properties hiervoor gebruiken is net zoiets als globale variabelen gebruiken in niet-OO talen.

  • Herko_ter_Horst
  • Registratie: November 2002
  • Niet online
Daos schreef op zaterdag 12 april 2014 @ 18:34:
[...]


Ik zet meestal commentaar boven de stukjes code. Het wordt dan zoiets:
code:
1
2
3
4
5
6
7
8
9
// determine whether to show not supported popup
supportExpired = date > new date(2014,4,8)
warnedLately = (date.now - popup.lastdisplayedtime).totalSeconds < 3600
shouldDisplayNoLongerSupported = supportExpired && !warnedLately && !popup.visible

// popup if necessary
if (shouldDisplayNoLongerSupported) then
  //display XP is no longer supported popup
/if
Zulk commentaar boven een paar regels code is een goede indicator dat die code ook in een aparte methode kan staan. "//popup if necessary" vind ik dan weer compleet overbodig commentaar: de code vertelt al precies en duidelijk leesbaar wat er gebeurt. Maar als die code binnen de 'if' langer is dan een handvol regels, dan kan dat ook wel weer duiden op een situatie waarin een aparte methode goed van pas komt.

Door kleinere methodes te maken, is het ook veel makkelijker in te zien waar de meeste complexiteit zit om te testen.
Properties gebruik ik voor het bewaren van de toestand van een object voor de buitenwereld. Intern voor de communicatie tussen methods/functies gebruik ik argumenten/parameters en return values. Properties hiervoor gebruiken is net zoiets als globale variabelen gebruiken in niet-OO talen.
Huh? Volgens mij sla je nu door in information hiding. De eenheid van encapsulation in de OO-wereld is de class. Een class bestaat uit state en methods. State is in principe verborgen voor de buitenwereld. Maar intern in de class kun je/moet je hier gewoon gebruik van maken. Een property is niets meer dan interne state die direct van buitenaf gemanipuleerd kan worden. Die hoef je intern echt niet via parameters/return values rond te passen, dat levert alleen maar overhead op. De vergelijking met globale variabelen is wel heel scheef. Misschien wordt dit gevoel veroorzaakt door te grote classes met teveel verantwoordelijkheden?

"Any sufficiently advanced technology is indistinguishable from magic."


  • Hydra
  • Registratie: September 2000
  • Laatst online: 06-10 13:59
Daos schreef op zaterdag 12 april 2014 @ 16:27:
Bedankt voor je constructieve bijdrage aan dit topic }:|
In plaats van snibbig te doen kun je ook gewoon om verduidelijking vragen natuurlijk. Hoewel hij hiet niet uitlegde heeft hij wel gelijk; korte functies zijn over het algemeen beter dan lange omdat ze simpeler zijn. Simpele functies zijn makkelijker mentaal te modelleren en is dus de kans dat je fouten maakt kleiner. Daarnaast zijn ze makkelijker te unit-testen.

https://niels.nu


  • Haan
  • Registratie: Februari 2004
  • Laatst online: 15:31

Haan

dotnetter

Hydra schreef op zondag 13 april 2014 @ 12:12:
[..]
Simpele functies zijn makkelijker mentaal te modelleren en is dus de kans dat je fouten maakt kleiner. Daarnaast zijn ze makkelijker te unit-testen.
Alleen als je een publieke functie op gaat delen in meerdere private functies, krijg je weer de discussie of je alleen de publieke functies moet unit tests of ook private functies ;)

Wat betreft het testen van private methods in .NET, daarvoor is PrivateObject erg handig.
C#:
1
2
3
4
Foo foo = new Foo();
PrivateObject privObj = new PrivateObject(foo);

var result = privObj.Invoke("SomePrivateMethod"); // eventueel met een params array voor parameters

Kater? Eerst water, de rest komt later


  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

Je moet het testen niet overdrijven, en al helemaal geen specifieke implementaties gaan testen. Een unit test is er om een blackbox van functionaliteit te testen. In dit geval dus of gegevens de pre-condities het resulteert in de gewenste post-conditie. Als je op een ander algoritme overgaat zou je je tests niet aan hoeven passen. Wat heb je er anders aan? Dat is juist het idee; dat als je code verandert je test of het nog steeds werkt. Natuurlijk werken die kleine stukjes die je zelf hebt geschreven. Bovendien heb je net zoveel kans op een bug daarin als in je test.
Pagina: 1