Övningsuppgift 8, Mer om enhetstest

Relevanta dokument
Att skriva till och läsa från terminalfönstret

Föreläsnings 9 - Exceptions, I/O

Lite om felhantering och Exceptions Mer om variabler och parametrar Fält (eng array) och klassen ArrayList.

Objektorienterad programmering i Java Undantag Sven-Olof Nyström Uppsala Universitet Skansholm: Kapitel 11

Parallellism, återblick

Idag. Exempel, version 2. Exempel, version 3. Ett lite större exempel

Kopiering av objekt i Java

Lösningar för tenta 2 DAT043,

Grundkurs i programmering, 6 hp (725G61) Dugga 2 tillfälle 2

Trådar. Aktiva objekt

Fördjupad Java. Undantagshantering. Fel

Objektorienterad programmering i Java Undantag Sven-Olof Nyström Uppsala Universitet Skansholm: Kapitel 11

Föreläsning 3 Innehåll. Generiska klasser. Icke-generisk lista ArrayList, skiss av implementering. Icke-generisk lista Risk för fel

LUNDS TEKNISKA HÖGSKOLA EDAA01 Programmeringsteknik fördjupningskurs Institutionen för datavetenskap HT 2015

Institutionen för datavetenskap HT /2008. Testning med JUnit

Föreläsning 12. Föreläsning 12. Rörliga figurer Klassen Timer Undantag Något om applets. Rörliga appletsfigurer Klassen Timer Undantag

Föreläsning 8 - del 2: Objektorienterad programmering - avancerat

FÖRSLAG TILL LÖSNINGAR FÖR TENTAMEN I INTERNETPROGRAMMERING MED JAVA, 5p för SY , kl

Objektorienterad programmering i Java, datastrukturer och algoritmer. Föreläsning 4 Jonas Lindgren, Institutionen för Datavetenskap, LiU

Undantagshantering. Fördjupad Java. Fel. Undantag. Fånga Undantag. Grupper av Undantag

Undantagshantering. Fördjupad Java. Undantag. Fel. Grupper av Undantag. Fånga Undantag

TENTAMEN PROGRAMMERINGSMETODIK MOMENT 2 - JAVA, 4P

Javas Exceptions. DD2385 Programutvecklingsteknik Fler bilder till föreläsning 7 23/ Kort om Javas Exceptions Trådar i Java

DI-institutionen Sid 1 av 6 Hans-Edy Mårtensson Sten Sundin

TDDE10 m.fl. Objektorienterad programmering i Java Föreläsning 6 Erik Nilsson, Institutionen för Datavetenskap, LiU

Några principer för effektiv enhetstestning

Parsing med Recursive Descent, Avbildningsklasser. Syntaxdiagram. Syntaxdiagram och kodning expression. Betrakta följande uttryck

Programmering med Java. Grunderna. Programspråket Java. Programmering med Java. Källkodsexempel. Java API-exempel In- och utmatning.

F8 - Arv. ID1004 Objektorienterad programmering Fredrik Kilander

PROGRAMMERINGSTEKNIK TIN212

DIAGNOSTISKT PROV. Tid. Hjälpmedel. Antaganden. Rättning. Övrigt. Diagnostiskt Prov. Klockan Inga

LÖSNINGSFÖRSLAG Programmeringsteknik För Ing. - Java, 5p

DAT043 - Föreläsning 7

Föreläsning 4. ADT Kö Kö JCF Kö implementerad med en cirkulär array Kö implementerad med en länkad lista

TENTAMEN: Objektorienterad programmering. Läs detta! Skriv din tentamenskod på varje blad (så att vi inte slarvar bort dem).

Felhantering TDDD78, TDDE30, 729A

Testning av program. Verklig modell för programutveckling

1 Uppgift 1. a) Skapar ett Company-objekt med hjälp av den överlagrade konstruktorn. Du kan själv välja värden på instansvariablerna.

Omtentamen för TDA540 Objektorienterad Programmering. Institutionen för Datavetenskap CTH HT-17, TDA540. Dag: , Tid:

Tentamen ID1004 Objektorienterad programmering May 29, 2012

TDDC30. Objektorienterad programmering i Java, datastrukturer och algoritmer. Föreläsning 4 Erik Nilsson, Institutionen för Datavetenskap, LiU

Lösningsförslag till tentamen i OOP, HI1027 Fredag 21 oktober 2011

OOP Objekt-orienterad programmering

Klassen javax.swing.timer

Överlagring, static, testning, formella metoder och undantag! Förelasning 13!! TDA540 Objektorienterad Programmering!

Typkonvertering. Java versus C

Objektorienterad programmering i Java

TENTAMEN I PROGRAMMERING. På tentamen ges graderade betyg:. 3:a 24 poäng, 4:a 36 poäng och 5:a 48 poäng

Språkkonventioner och redigering av tal.

Undantag. Engelska: exceptions. Skansholm: exceptionella händelser

DAT043 Objektorienterad Programmering

TDDE10 m.fl. Objektorienterad programmering i Java Föreläsning 6 Erik Nilsson, Institutionen för Datavetenskap, LiU

Tentamen LÖSNINGSFÖRSLAG. c) Tilldelningen C x = new D() ger kompileringsfel eftersom klassen D är abstrakt.

Objektorienterad Programmering (TDDC77)

Classes och Interfaces, Objects och References, Initialization

Föreläsning 3. Stack

Command line argumenter. Objektorienterad Programmering (TDDC77) Vad blir resultatet? Nu då? Ahmed Rezine. Hösttermin 2016

/* * * Lösningsförslag tentamen DIT950 * Datum * */ /* * -1 - */ För samtliga gäller,se föreläsningsanteckningar.

Dagens program. Programmeringsteknik och Matlab. Objektorienterad programmering. Vad är vitsen med att ha både metoder och data i objekten?

Concurrency Saker händer samtidigt. Process En instans av ett program

Kort repetition. Programmeringsteknik för Bio1 och I1. Vad ska vi lära oss idag? Ett exempel

TDDC30. Objektorienterad programmering i Java, datastrukturer och algoritmer. Föreläsning 11 Jonas Lindgren, Institutionen för Datavetenskap, LiU

Laboration 3, uppgift En klass för en räknare

Tung bakgrundsaktivitet t.ex. Aktiva objekt t.ex. Animering, simulering. DD2385 Programutvecklingsteknik Några bilder till föreläsning 9 6/5 2013

Objektorienterad Programkonstruktion. Föreläsning 4 8 nov 2016

Objekt, Klasser, Paket m. m.

if (n==null) { return null; } else { return new Node(n.data, copy(n.next));

Objektorienterad Programkonstruktion. Föreläsning 11 6 dec 2016

Testning och felhantering

Tentamen. DD2385 Programutvecklingsteknik vt 2013 Onsdagen den 22 maj 2013 kl Hjälpmedel: penna, suddgummi, linjal

Felhantering. Andra brott mot språkets regler. Man kan också i programmet bryta mot ett antal olika regler som gäller. Exempelvis:

Övningsuppgift. Bankkonton. Steg 2. Författare: Mats Loock Kurs: Inledande programmering med C# Kurskod:1DV402

Inledande programmering med C# (1DV402) Tärningarna ska kastas

Kungl. Tekn. Högskolan Förel 1, bild 1 Föreläsning 1: Introduktion ffl Kursinnehåll ffl Javarepetition ffl Referenser ffl Nyckelordet static ffl Klass

Chapter 4: Writing Classes/ Att skriva egna klasser.

1 Comparator & Comparable

Hämta data mha URLer Föreläsning 2b. Innehåll Klassen URL

Objektorienterad programmering

Övning vecka 6. public void method2() { //code block C method3(); //code block D }//method2

Lösningsförslag till tentamen

Tommy Färnqvist, IDA, Linköpings universitet

Software Technology. Josef Svenningsson

Algoritmer. Två gränssnitt

Det är principer och idéer som är viktiga. Skriv så att du övertygar rättaren om att du har förstått dessa även om detaljer kan vara felaktiga.

Objekt, klasser. Tillstånd Signatur Kommunikation Typ. Fält, parametrar och lokala variabler. Konstruktorer Metoder DAVA15

Läs detta! Uppgifterna är inte avsiktligt ordnade efter svårighetsgrad. Skriv ditt idnummer på varje blad (så att vi inte slarvar bort dem).

Föreläsning 4 Innehåll. Abstrakta datatypen lista. Implementering av listor. Abstrakt datatypen lista. Abstrakt datatyp

Föreläsning 11 Aktiva objekt och trådar, strömmar, kommunikation DAT043,

Översikt. Installation av EasyPHP 1. Ladda ner från Jag använder Release Installera EasyPHP.

Instuderingsuppgifter läsvecka 6 - LÖSNINGAR

UML. Översikt UML. Relationer mellan klasser. A är ett aggregerat av B:n. Kontor aggregat av Enheter. 12 olika diagramtyper, bl.a.

Tentamen i Objektorienterad programmering

TDA550 Objektorienterad programvaruutveckling IT, forts. kurs Övning vecka 3

Kapitel 6 - Undantag

Föreläsning 2, vecka 8: Repetition

Inlämningsuppgift MiniPlotter

Tentamen Programmering fortsättningskurs DIT950

//Använd main som ett "handtag" för att hålla ihop programmet. //Själva programmet finns i övriga klasser.

LÖSNINGSFÖRSLAG TILL TENTAMEN PROGRAMMERINGSMETODIK MOM2 - JAVA, 4P.

TDA550 Objektorienterad programvaruutveckling IT, forts. kurs Övning vecka 2

Transkript:

Nedan beskrivs ett antal fall där det är mer eller mindre svårt att skriva enhetstester. Det beskrivs också hur problemen kan lösas. Övningsuppgiften är att skriva enhetstester för så många av dessa fall som hinns med under övningen. Fall1, Att testa en metods felhantering Testa att metoder kastar undantag när de ska. Antag att följande metod ska testas: public void themethod(int theparameter) { if (theparameter < 0) { throw new IllegalArgumentException( theparameter = + theparameter + but must not be negative. ); //some task performed by the method. Då är det lämpligt att (förutom tester av metodens uppgift) även skriva ett test i stil med: //Junit 3.8 public void testthemethodserrorhandling() { try { themethod( 1); fail("no IllegalArgumentException was thrown"); catch (IllegalArgumentException success) { assertequals(success.getmessage(), theparameter = 1 + but must not + be negative. ); //Junit 4.0 @Test(expected=IllegalArgumentException.class) public void testthemethodserrorhandling() { try { themethod( 1); catch (IllegalArgumentException success) { assertequals(success.getmessage(), theparameter = 1 + but must not + be negative. ); Det kan också finnas indata till metoden som inte är tillåtet men som heller inte hanteras på något sätt. Antag till exempel att vi har skrivit en metod som returnerar det n:e tecknet i en array: public int get(int[] array, int index) { return array[index]; Om index är negativt eller större än arrayens längd kommer metoden inte att fungera. Vad som kommer att hända är däremot inte definierat i koden. I det fallet ska inget test skrivas med ogiltigt index eftersom alla svar får förekomma. Om en metod skrivs på så sätt måste det tydligt framgå i dess kommentar att resultatet är odefinierat om index är negativt eller större än arrayens längd. 1

Uppgift: En klass Account (som beskriver ett bankkkonto) har en metod withdraw(int amount). Om beloppet som ska tas ut är större än behållningen på kontot kastas ett OverdraftException med lämplig förklarande text. Skriv ett test för detta (klasserna Account och OverdraftException finns nedan). public class Account { private int balance; public Account(int balance) { this.balance = balance; public void withdraw(int amount) throws OverdraftException { if (amount > balance) { throw new OverdraftException("Can not withdraw " + amount + " when balance is " + balance); balance = amount;... public class OverdraftException extends Exception { public OverdraftException(String msg) { super(msg); Fall 2, Klassen som ska testas har komplicerade beroenden Oftast visar sig detta problem när en klass i ett lager högt upp ska testas. Antag att vi ska testa klassen Controller i programmet i figur 1. Om vi skriver enhetstester för den på vanligt sätt kommer vi att testa inte bara metoden i Controller utan även de metoder den anropar i AClassInTheModel, metoder som den senare anropar i SomeDBFacade och själva databasanropet. Om testet misslyckas är det inte lätta att veta var felet finns. Detta problem kan lösas på två olika sätt: 2

1. Mitt eget ovedertagna sätt (beskrivs inte figur). Skriv enhetstester för alla klasser i anropskedjan, dvs Controller, AClassInTheModel och SomeDBFacade. Gör inget mer utan låt alla tester exekvera ända ner till databasen. Den nedersta klass där ett test fallerar är den där problemet finns. Rätta felen där först och kolla sedan om även tester av högre liggande klasser passerar. Fördelen med denna metod är att den inte kräver något arbete förutom att skriva enhetstesterna. Den kräver heller inte några förändringar i koden som testas. Det är alltså en snabb metod som lämnar produktionskoden opåverkad. Det blir även mindre kod att underhålla än med metod 2 nedan. 2. Den vanligtvis föreslagna metoden (figur 2). Skapa ett interface för klassen närmast under den som ska testas, dvs för AclassInTheModel i det här fallet. Skriv sedan en dummyklass (mock class) som implementerar interfacet men inte gör något annat än att returnera lämpligt data där så krävs. När testet körs får Controller en referens till MockClass i stället för AClassInTheModel. Då blir det verkligen bara Controller som testat och inget annat. Fördelar med denna metod är att vi får full koll på vad som testas och det blir därför lättare att veta var eventuella fel finns. Vi har också större möjlighet figur 2 figur 1 att påverka vad de testade metoderna ska ge för svar eftersom svaren hårdkodas i MockClass. Denna metod funkar även bra om vi vill undvika ett nätverksanrop. Om AClassInTheModel hade gjort ett sådant hade metod 1 ovan varit olämplig eftersom alla enhetstester av Controller då skulle inkludera nätverksanropet vilka skulle vara lite väl långsamt och oförutsägbart. Ett par punkter att tänka på om denna metod används: Placera MockClass bland testklasserna och interfacet i produktionskoden. Interfacet behöver inte innehålla alla metoder i AClassInTheModel, det räcker med de metoder som används av de testade metoderna i Controller. Den här metoden förändrade produktionskoden. Det behöver dock inte vara så illa så länge dess design inte försämras. Att ha infört ett interface stör oftast inte designen, men det är lämpligt att ta sig en ordentlig funderare på vilka metoder från AClassInTheModel som ger ett interface med hög sammanhållning (high cohesion). 3

Uppgift: Om AClassInTheModel innehåller statiska metoder går det inte att flytta deras definitioner till interfacet. Då får vi helt enkelt kopiera dem till MockClass. Skriv enhetstest för Controller i figur 1. Använd metod 2. De inblandade klasserna finns nedan. public class Controller { private AClassInTheModel model; public Controller(AClassInTheModel model) { this.model = model; public void dosomething() { model.methodinthemodel();... public class AClassInTheModel { private SomeDBFacade db; public AClassInTheModel(SomeDBFacade db) { this.db = db; public void methodinthemodel() { db.methodinthedblayer();... public class SomeDBFacade { public void methodinthedblayer() { //Call to the database. Fall 3, Det är svårt att få reda på den testade metodens resultat Ibland är det svårt få reda på resultatet av en metods arbete. Det kan bero på många olika saker: 1. Den uppdaterar en databas och inte tillståndet i ett objekt. 2. Den uppdaterar ett användargränssnitt och inte tillståndet i ett objekt. 3. Den öppnar ett användargränssnitt för att låta användaren mata in data. 4. Den uppdaterar tillståndet i ett objekt som inte har någon metod som returnerar det tillståndet. Detta är förmodligen den vanligaste orsaken. 4

Oavsett vad anledningen är, skriv aldrig nya get metoder för att underlätta testningen. Det är nämligen högst sannolikt att de försämrar produktionskodens design, tex genom att lämna ut mer data än nödvändigt och därmed bryta inkapslingen. Här kommer några olika sätt att lösa problemet: 1. Det hjälper ofta att ta sig en rejäl funderare på hur metoden under testning påverkar programmets beteende. Någonstans måste tillståndsförändringen förändra beteendet, annars vore den meningslös. Förmodligen finns det någon metod eller någon kombination av metoder (i samma objekt eller något annat) som kan visa om metoden under test har utfört sin uppgift rätt. 2. Det kanske faktiskt är en bugg att get metoden saknas. Det är förstås inte förbjudet att skriva get metoder, det viktiga är att de verkligen behövs i produktionskoden och inte skrivs bara för att underlätta testet. 3. Vi kan skriva en subklass till klassen under test och införa extra get metoder i den, sedan kör vi testet mot den. Subklassen ska då ligga bland testklasserna. Detta funkar inte om det sökta tillståndet finns i privata fält. 4. Kanske kan vi strunta i metoden vi försöker testa och nöja oss med att testa någon annan metod som använder den. Om den funkar funkar säkert också metoden vi först ville testa. Det är inte direkt enhetstest men jag föredrar det framför att förstöra designen av produktionskoden. 5. Svårigheten kan bero på att metoden under test gör för mycket och borde delas upp. 6. Om problemet är punkt ett, att tillståndet finns i en databas, kan testet läsa resultatet direkt ur databasen. 7. Om problemet är punkt två behövs ett ramverk för att testa användargränssnitt. Mer om det i fall sju nedan. 8. Om problemet är punkt tre pekar det kanske på dålig design av produktionskoden. Samma metod borde inte göra så mycket. Förmodligen är det lämpligt att dela upp den för att få en bättre design, kanske blir den då också lättare att testa. Om den faktiskt ska göra det den gör krävs ett ramverk för att testa användargränssnitt. Mer om det i fall sju nedan. Uppgift: Nedanstående kod är ett enkelt tidtagarur. Skriv enhetstester för metoderna start() och stop(). public class Timer { private long starttime; public void start() { starttime = System.currentTimeMillis(); public long stop() { return System.currentTimeMillis() starttime; 5

Fall 4, Det är svårt att ge indata till testet Det kan vara svårt att ge indata till ett test på grund av att metoden som ska testas opererar på data som inte hanteras av objektet under test. Datat hämtas i stället från till exempel en fil, en databas eller en komplex historia av andra objekt som är väldigt krångliga att instasiera på rätt sätt. Här är några förslag på åtgärder: 1. Om datat läses från en fil kan en sådan med lämpligt data finnas med i testkoden. Den kan kopieras till lämpligt ställe i filstrukturen av byggscriptet (tex ant). Ett annat alternativ är att själva testet skriver en fil med rätt innehåll. Kom ihåg att tester inte får lämna skräp efter sig, testet (eller möjligtvis byggscriptet) måste alltså ta bort filen när testet är klart. 2. Om datat läses från en databas kan byggscriptet eller testet fylla den med lämpligt data. Kom ihåg att tester inte får lämna skräp efter sig, testet (eller möjligtvis byggscriptet) måste alltså ta rensa databasen när testet är klart. 3. Om datat kommer från andra objekt är det lämpligt att ersätta dem med mock objects enligt metoden som beskrivs i fall två ovan. Dessa ska då returnera lämpligt data till objektet under test. 4. Datat kan vara slumpmässigt, tex i en klass som simulerar tärningskast. Ett sätt att hantera det är att anropa metoden i fråga många gånger och kolla att utfallen har en bra fördelning. Om det slumpmässiga datat är indata till en mer komplex algoritm och vi vill testa att algoritmen räknar rätt kan det vara lämpligare att subklassa den aktuella klassen och överlagra genereringen av indatat med ett hårdkodat data. I så fall ska subklassen ligga bland testklasserna. Om det slumpmässiga datat kommer från ett annat objekt kan det objektet ersättas med ett mock objekt som ger hårdkodat data. Uppgift: Nedanstående kod räknar hur många gånger en viss token finns i en fil. Skriv enhetstest för den. Tänk på att privata metoder alltid blir testade om du testar de publika (och paketprivata) metoderna. 6

import java.io.bufferedreader; import java.io.filereader; import java.io.ioexception; public class FileAnalyzer { private BufferedReader in; public FileAnalyzer(String filepath) throws IOException { in = new BufferedReader(new FileReader(filePath)); public int counttokens(string token) { String readline = null; int appearances = 0; try { while ((readline = in.readline())!= null) { appearances += counttokensinonestring(readline, token); catch (IOException ioe) { ioe.printstacktrace(); return appearances; private int counttokensinonestring(string s, String token) { int appearances = 0; for (int offset=0; offset<s.length(); offset++) { if (s.startswith(token, offset)) { appearances++; return appearances; Fall 5, Det är trådar som ska testas 1. Vi vill testa ett arbete som utförs av en tråd. Det innebär att vi inte får returvärdet direkt som vid ett metodanrop. I stället måste vi vänta på att tråden utför sitt arbete och sedan på något sätt hämta resultatet. Om tråden dör när arbetet är klart kan testet helt enkelt anropa join() på tråden och sedan kolla resultatet. Om objektet under test inte har någon metod som lämnar ut tråden måste testklassen hämta en trådreferens från java motorn. Det kan göras med följande kod: ThreadGroup tg = Thread.currentThread().getThreadGroup(); Thread[] threads = new Thread[tg.activeCount()]; tg.enumerate(threads); for (Thread t : threads) { if (!(t == Thread.currentThread())) { t.join(); Om testest inte kan vänta på att tråden dör måste testet tolka datat så snart trådenskrivit det, då krävs lämplig händelsesynkronisering mellan tråden som ska testas och tråden med testet. 7

2. Denna punkt ingår inte i övningen. Vi vill testa själva trådsäkerheten (att dödlägen inte uppstår, att det inte blir kapplöpning osv). För att göra detta är det lämpligt att använda ett ramverk för trådtestning. Det finns många sådan, tex mtunit och JMTUnit. Se även denna artikelserie (som kortfattat förklarar test av trådsäkerhet och introducerar ett enkelt ramverk): http://www.ftponline.com/javapro/2003_11/online/rnettleton_11_26_03/ http://www.ftponline.com/javapro/2003_12/online/rnettleton_12_02_03/ http://www.ftponline.com/javapro/2003_12/online/rnettleton_12_10_03/ Uppgift (Gäller punkt 1 ovan): Skriv ett enhetstest för nedanstående klass, vilken fungerar som följer. 1. Ett nytt objekt skapas, CalculatingThread ct = new CalculatingThread; 2. Metoden startworker anropas (ct.startworker()). Då startar en tråd som adderar 3 till sum 10 8 gånger. 3. När tråden har dött kan värdet av sum läsas med ct.getsum(). Testet ska testa att detta värde är 3 10 8. public class CalculatingThread { private int sum = 0; public int getsum() { return sum; public void startworker() { Runnable r = new Runnable() { public void run() { for (int i=0; i<100000000; i++) { sum += 3; ; Thread t = new Thread(r); t.start(); Fall 6, Testet påverkar databasen Ingår inte i övningen, jag vill bara påminna om att tester ska vara isolerade. De får alltså inte lämna skräp efter sig i databasen eller bygga på att något redan finns där när de startar. Det är heller inte lämpligt att olika tester körs samtidigt mot samma databas. Fall 7, Det är ett grafiskt användargränssnitt som ska testas Detta ingår inte i övningen. För att testa ett grafiskt användargränssnitt behövs ett ramverk, till exempel för att simulera mus och tangentbordshändelser. Det finns många sådana, ett som är helt OK är JFCUnit. Att skriva enhetstester för grafiska användargränsnitt är svårt. Främst, tror jag, pga att en människa uppfattar ett användargrässnitt på ett sätt som är på tok för intelligent och komplext 8

för att kunna uttryckas i något så primitivt som ett program. Programmet blir alldeles för exakt. Ett av symptomen på detta är att användargränssnitt ofta ändras. För en människa är funktionaliteten densamma om tex ett diagram får en skugga bakom staplarna, men för ett program som testar om bilden ser ut på ett visst sätt blir det heltannorlunda. Redovisning Redovisa skriftligt per mail till leifl@kth.se de uppgifter som är klara när övningen är slut. Minst en färdig uppgift måste redovisas. 9