Software Testing User Interface Testing User Interface Testing 1 Sistemi basati sugli eventi • Le interfacce utente rappresentano un caso di sistema ad eventi – Il sistema è in uno stato stabile fino all’intervenire di un evento utente, che fa partire un codice di event handling • Anche un calcolatore, dotato di sistema delle interruzioni, può essere considerato come un sistema ad eventi – Un segnale è in grado di avviare un’interruzione, che viene successivamente servita • I moderni sistemi distribuiti a sensori devono essere considerati sistemi ad eventi – Ogni dispositivo è in grado sia di ricevere input, che di elaborarli User Interface Testing 2 Testing di sistemi interattivi • Il testing di sistemi interattivi (in particolare di interfacce utente) viene condotto tipicamente in maniera black box • Tipicamente i casi di test sono progettati tenendo in conto le possibili interazioni che un utente può eseguire sull’interfaccia utente – Per poter approcciare il problema del testing è fondamentale la disponibilità di un modello descrittivo delle interazioni utentemacchina – Quali modelli sono utilizzabili per modellare UI? User Interface Testing 3 Modellazione delle interazioni • Il modello più comune è un modello di Macchina a stati: – Gli elementi fondamentali di una UI sono: • Finestre e relativi Widget, dotati di – Eventi che possono essere eseguiti dagli utenti – Campi, che possono essere eventualmente settati • Si suppone che l’interfaccia utente non esegua alcuna operazione se non in seguito a sollecitazioni da parte degli utenti – Lo “stato” dell’interfaccia utente viene modellato da uno stato di un automa – L’esecuzione di eventi sulla UI (es. Click su button…) viene modellata come ingressi impulsivi che scatenano transizioni nell’automa – Gli input immessi sono modellati come ingressi a livelli da cui dipende la transizione che si verifica User Interface Testing 4 Esempio- modello di FSM per una UI Click on "Add new film" Click on "Search" Se arch Movie Click on "Search" Logged As Admin Click on "Cancel" Click on "Back" List of Films Click on "film's link" Click on "Cancel" Click on "OK" Click on "film's link" Film Information Click on "Back" Click on "Insert" Click on "Cancel" Film Information + list of film Click on "Back" Movie Inserted Film Information + Search Movie Es.: L’FSM che descrive l’evoluzione di una UI per l’ esecuzione di un Caso d’Uso User Interface Testing 5 Testing delle interfacce • Gli input dei casi di test devono essere indicati sotto forma di sequenze di valori di input ed eventi da eseguire. – A seconda dei criteri di copertura (es. Stati, Eventi, Transizioni, path…), si progetteranno diversi TC – Es.: click (add-film),click(search), click(film link),click(insert), click(OK) è il TC che permette di eseguire il cammino in rosso (Scenario: Inserimento Film OK) • Ad una sequenza di valori di input dovrebbe corrispondere una sequenza di stati visitati e di output riscontrati. User Interface Testing 6 Esempio- modello di FSM per una UI Click on "Add new film" Click on "Search" Se arch Movie Click on "Search" Logged As Admin Click on "Cancel" Click on "Back" List of Films Click on "film's link" Click on "Cancel" Click on "OK" Click on "film's link" Film Information Click on "Back" Click on "Insert" Click on "Cancel" Film Information + list of film Click on "Back" Movie Inserted Film Information + Search Movie Es.: Cammino di esecuzione che esercita un certo scenario del Caso d’Uso User Interface Testing 7 Possibile problema • Come ottenere un FSM che descriva adeguatamente l’UI e che possa essere usato per progettare i casi di Test? • Due possibilità: – FSM prodotto in fase di sviluppo dell’applicazione (ma può essere poco aderente all’effettiva implementazione fatta dell’UI) – FSM ricostruito per Reverse Engineering a partire dalla UI effettivamente implementata (richiede tecniche di analisi statica o dinamica) – Nel caso di Interfacce Dinamicamente Configurabili (es. di Rich Internet Applications) piuttosto che Statiche, l’analisi statica non è sufficiente! Necessità di definire tecniche e strumenti di RE per la generazione (semi-automatica) del modello. User Interface Testing 8 Esempio in Java public JMenu createOptionMenu() { JMenu m = new JMenu("Scientifica"); JMenuItem item = new JMenuItem(); Menu generato a run time Voce di Menu generata a run time class itemListener implements ActionListener Ascoltatore dichiarato a run time { public void actionPerformed(ActionEvent e) Codice dell’ascoltatore dichiarato a run time { try{ op1 = Double.parseDouble(text.getText()); } catch(NumberFormatException ecc) { text.setText("Errore: nessun valore inserito!"); return; } operazione = RADICE_QUADRATA; invio.doClick(); } } … } Non è, in generale, possibile, scrivere metodi di test che si riferiscono a oggetti, classi e metodi che vengono dichiarati a run-time User Interface Testing 9 Problema degli stati equivalenti • Per poter astrarre un modello a stati è necessario poter decidere quando due stati coincidono – Siccome qualsiasi valore di qualsiasi variabile può caratterizzare lo stato, il numero di stati possibili è potenzialmente illimitato – Per poter implementare dei criteri di equivalenza è necessario selezionare piccoli sottoinsiemi di informazioni, ma così facendo si rischia di non considerare informazioni importanti • Nel nostro caso, una tecnica consiste nel considerare solo informazioni relative all’interfaccia utente, non allo stato interno dell’applicazione • Da un modello FSM con un numero possibile di stati, è possibile ricavare test suite che vanno a coprire tutti i nodi o tutti gli archi – Un oracolo è dato dall’equivalenza dello stato raggiunto in ogni istante del test con quello descritto dal modello User Interface Testing 10 Modello Event Interaction Graph • Un altro modello utilizzato in letteratura è l’EIG (Event Interaction Graph) – Un grafo i cui nodi sono eventi e i cui archi orientati rappresentano relazioni di sequenzialità tra eventi – Un cammino su un EIG corrisponde ad una sequenza di eventi, quindi ad un caso di test possibile – Problema: l’EIG è, potenzialmente, illimitato • Si possono generare test suite che coprono l’EIG a meno di un certo valore massimo di profondità (lunghezza) del caso di test • L’unico oracolo automatico è dato dalla possibilità di eseguire effettivamente l’evento seguente User Interface Testing 11 Automatizzazione • Per poter eseguire automaticamente casi di test su interfacce utente sono necessari: – Strumenti che consentano di emulare il comportamento dell’utente, in particolare: • Settare valori di input • Scatenare eventi – Strumenti che consentano di analizzare le interfacce restituite • Tramite asserzioni sui widget presenti nell’interfaccia e I loro valori User Interface Testing 12 Un esempio: Selenium • Consideriamo il framework Selenium, a supporto del testing di interfacce utente di applicazioni Web – http://selenium.openqa.org/ • Selenium offre quattro modalità di utilizzo: – – – – Selenium Selenium Selenium Selenium IDE Core Remote Control Grid User Interface Testing 13 Selenium IDE • Si tratta di un’estensione di un browser che consente di: – catturare le interazioni tra un utente e una applicazione web (fase di Capture) – “suggerire” asserzioni relative alla presenza di widget sull’interfaccia utente – replicare l’esecuzione di casi di test, mantenendo un log degli esiti dei test (fase di Replay) • Selenium è dunque usabile per progettare TC anche a prescindere da un modello formalizzato (es. FSM) della UI. • Utile per l’esecuzione di Testing di Accettazione User Interface Testing 14 Capture In fase di capture, Selenium IDE mantiene un log delle operazioni effettuate dall’utente e delle asserzioni da egli proposte User Interface Testing 15 Replay In fase di replay, Selenium IDE esegue automaticamente test generati in fase di capture, mantenendo statistiche sul numero di test terminati con successo e falliti User Interface Testing 16 Codice generato In fase di capture, Selenium IDE genera anche del codice sorgente (a scelta in Java, C#, Perl, PHP, Python o Ruby) che può essere eseguito indipendentemente da Selenium IDE Il codice generato necessita, per essere eseguito, di packages forniti con Selenium (che formano il Selenium Core) User Interface Testing 17 "Twenty percent of all input forms filled out by people contain bad data." - Dennis Ritchie, More Programming Pearls: Confessions of a Coder by Jon Louis Bentley User Interface Testing 18 Validazione dell’input • Un tipico problema del testing delle interfacce utente è la verifica della validità dei dati di input – E’ la causa di molti attacchi (exploit) contro le applicazioni – Ad esempio, in linguaggi a puntatori, come C, l’immissione di una stringa troppo lunga in input, in mancanza di validazione, può portare a sovrascrivere altri dati o addirittura zone di codice del programma stesso – In linguaggi interpretati, come quelli spesso usati per il web, il problema è ancora più sentito User Interface Testing 19 Il problema della validazione degli input La validazione dei dati può essere fatta sia sul lato client che sul lato server La validazione sul lato client ha il vantaggio di utilizzare tempo di CPU della macchina client piuttosto che quello della macchina server La validazione sul lato client può essere però scavalcata da un utente malintenzionato che vada a sollecitare il server con una richiesta http che non sia passata per la pagina client: in questo caso il server potrebbe avere delle anomalie La soluzione più corretta è quella di porre la validazione sia sul lato client che sul lato server, in modo da bloccare la maggior parte delle richieste scorrette sul client (risparmiando risorse sul server). Solo le richieste fraudolente verrebbero così bloccate dalla validazione lato server User Interface Testing 20 Esempio: Cross-Site Scripting Si verifica quando uno script lato client, inserito maliziosamente in un campo di input, viene eseguito sulla macchina client di un utente ignaro Guestbook.asp Sign.html <% conn=OpenDBConnection rs=server.createobject("Adodb.recordset") rs.open "SELECT Message FROM GuestBook" , conn,3,3 %> Message=Server.HtmlEncode(Message) <table> <% rs.movefirst while not(rs.eof) Sign.asp response.write (rs.fields("Message")) <% Message=request.form("txtMessage") rs.movenext conn=OpenDBConnection wend set rs=server.createobject("Adodb.recordset") %> rs.open "Guestbook",conn,1,2,2 </table> rs.Addnew <% rs.close rs("Message")=Message set rs=nothing rs.update conn.close %> set conn=nothing %> <form method="post" action="sign.asp"> <textarea name="txtMessage"></textarea> <input type="submit" value="Sign!"> </form> User Interface Testing 21 Testing White Box di GUI • Il testing white box (o comunque la progettazione di classi di test di unità) per GUI è reso difficile da alcuni fattori: – Molto spesso le classi della GUI sono istanziate a runtime, così come i metodi ascoltatori degli eventi • Molto difficile scrivere dei driver che riescano ad emulare le stesse esecuzioni del programma originale – Problemi di concorrenza: i test agiscono in un thread separato da quello che crea l’interfaccia, per cui bisogna anche misurarsi con le difficoltà relative alla tempificazione delle azioni di test User Interface Testing 22 Java Robot (awt) • La classe Robot consente l’esecuzione programmatica di eventi sull’interfaccia utente (limitatamente ad awt) – Ha metodi che riproducono le azioni del mouse ed eventi che riproducono la pressione di tasti • Documentazione: http://download.oracle.com/javase/1.4.2/docs/api/java/ awt/Robot.html • Un tutorial: http://forum.codecall.net/javatutorials/25923-robot-class.html User Interface Testing 23 Esempio Java Robot import java.awt.AWTException; import java.awt.Robot; import java.awt.event.InputEvent; public class MovingMouseDemo { public static void main(String[] args) { try { Robot robot = new Robot(); robot.mouseMove(200, 200); robot.mousePress(InputEvent.BUTTON1_MASK); robot.mouseRelease(InputEvent.BUTTON1_MASK); robot.mouseWheel(-100); } catch (AWTException e) { e.printStackTrace(); } } } Il problema principale è quello di poter nominare gli oggetti dell’interfaccia (ad esempio InputEvent.BUTTON1_MASK) quando essi sono dinamicamente generati User Interface Testing 24 UISpec4J • UISpec4J è una libreria a supporto del testing funzionale e di unità di applicazioni Java con interfaccia utente basata su Swing, che fa uso di Junit • In pratica, mette a disposizione metodi e oggetti che consentono di interrogare direttamente gli elementi dell’interfaccia utente – Strumenti analoghi: librerie di Selenium per le interfacce client di WA e Robotium per Android • http://www.uispec4j.org/ User Interface Testing 25 Test Case in UI Spec • Una classe di test UISpec va ad estendere la classe UISpecTestCase – public class AddressBookTest extends UISpecTestCase • Per poter eseguire i test mantenendo un punto di controllo sull’applicazione sotto test, il metodo setup conterrà (il parametro String[0] rappresenta la linea di comando, in questo caso vuota, della chiamata dell’applicazione): – protected void setUp() throws Exception { setAdapter(new MainClassAdapter(Main.class, new String[0])); } User Interface Testing 26 Esempio: Calcolatrice (1/2) package Calcolator; import org.uispec4j.* public class TestWhiteBox extends UISpecTestCase { private Window main; protected void setUp() throws Exception { setAdapter(new MainClassAdapter(Starter.class, new String[0])); main = getMainWindow(); } • • • public void testSumOK() throws Exception{ Button num1 = main.getButton("4"); num1.click(); Button plus = main.getButton("+"); plus.click(); Button num2 = main.getButton("4"); num2.click(); Button equals = main.getButton("="); equals.click(); assertEquals("8.0",main.getTextBox ().getText()); } La finestra principale è ottenuta col metodo getMainWindow() I riferimenti ai Button sono ottenuti con getButton(String) in base all’etichetta che essi mostrano Il riferimento alla TextBox è ottenuto con getTextBox() approfittando della circostanza che essa è unica, in tale interfaccia User Interface Testing 27 Esempio: Calcolatrice (2/2) package Calcolator; import org.uispec4j.* public class TestWhiteBox extends UISpecTestCase { private Window main; protected void setUp() throws Exception { setAdapter(new MainClassAdapter(Starter.class, new String[0])); main = getMainWindow(); } • • public void testSenoNotOK() throws Exception{ Button numc1 = main.getButton("3"); numc1.click(); Button numc2 = main.getButton("."); numc2.click(); Button numc3 = main.getButton("1"); numc3.click(); Button numc4 = main.getButton("4"); numc4.click(); MenuItem menu= main.getMenuBar().getMenu("Scientifica"); menu.click(); MenuItem menu2= menu.getSubMenu("Seno"); menu2.click(); assertEquals("0.0015926529164868282",main.getT extBox().getText()); Il riferimento al menu è ottenuto con i metodi getMenuBar().getMenu(String), dove la String è l’etichetta visualizzata del Menu L’asserzione è una asserzione tra stringhe, per cui assertEquals è ben posto; se si fosse trattato di un confronto tra reali si sarebbe dovuto utilizzare un asserzione del tipo: – AssertTrue (Math.abs(valoreAtteso-valoreOttenuto)<epsilon) User Interface Testing 28 Esecuzione dei Test Case • L’esecuzione dei test si ottiene sfruttando il framework Junit • Ulteriori metodi possono essere personalizzati per poter individuare gli elementi dell’interfaccia in altri modi • E’ possibile interagire anche con i Dialog • Ulteriori informazioni sono disponibili nel tutorial all’indirizzo: http://www.uispec4j.org/tutorial User Interface Testing 29 Considerazioni su UISpec4J • UISpec4J è uno strumento molto semplice e abbastanza potente, a supporto del testing di interfacce utente Swing (le più diffuse) di applicazioni Java interattive • UISpec4J estende Junit e può essere utilizzato in tutti i processi nei quali viene utilizzato quest’ultimo – Progettazione di casi di test da parte dello sviluppatore, per il testing di unità – Progettazione di casi di test per il testing funzionale black box • UISpec4J si presta poco alla generazione automatica di casi di test, ma è naturalmente sempre utilizzabile per l’esecuzione automatica e la valutazione automatica dell’esito dei test User Interface Testing 30 Abbot e Costello • Abbot e Costello sono un’altra coppia di tool che aiuta nella programmazione di casi di test per interfacce utente Java (sia AWT che Swing). – Abbot è un insieme di librerie a supporto dell’esecuzione dei test case realizzati • Abbot può essere utilizzato in maniera analoga a UISpec4J – Costello è uno strumento interattivo che fornisce feature per il capture, l’editing, l’esecuzione, la visualizzazione dei risultati dei test ed altro http://abbot.sourceforge.net/doc/overview.shtml User Interface Testing 31 Uno scenario di utilizzo di Costello • Creazione di un nuovo caso di test 1. File/New Script: 2. Imposta classe e metodo di partenza e posizione del jar 3. Cattura/All Actions 4. Esegui un esempio di esecuzione sull’applicazione da testare 5. Premi Shift+F1 dopo aver posizionato il puntatore sul campo da usare per l’asserzione 6. Imposta un’asserzione 7. Termina 8. File/Save (in formato XML) User Interface Testing 32 Uno scenario di utilizzo di Costello • Esecuzione di un caso di test 1. File/New Script 2. Seleziona una test suite 3. Esegui 4. Verifica l’esito delle asserzioni User Interface Testing 33 Utilizzo di Abbot • Abbot da solo può essere utilizzato in maniera simile ad UISpec4J, per scrivere test, in particolare anche test di unità di singoli componenti: User Interface Testing 34 Android Testing: generalità Un’applicazione Android in senso lato è composta di un lato client e di una o più tipologie di risorse lato server – Web Applications, Web Services, Risorse REST … Ci si limiterà allo studio delle problematiche del testing della parte client, la cosiddetta app Una app Android è sostanzialmente un’applicazione interattiva sottoposta a: – Eventi utente (eventi touch, segnali da sensori) – Eventi di sistema (interruzioni, segnali broadcast) User Interface Testing 35 Android Testing: generalità E’ necessario definire, adattandoli all’ambiente Android: – test models, per rappresentare le tipologie di elementi e interazioni – – – – da considerare e procare; testing levels, che specifichino i diversi punti di vista e obiettivi rispetto ai quali viene progettato il testing; test strategies, che definiscono obiettivi, euristiche e algoritmi da seguire nella progettazione dei casi di test; testing processes, che definiscono le modalità di esecuzione dei processi per il testing delle applicazioni Android; testing tools, strumenti a supporto delle attività di testing, in particolare a supporto della loro automazione User Interface Testing 36 Unit Testing Che cosa è possibile considerare come unità, nel testing di un’applicazione Android? – – – – – Activity Service Broadcast Receiver Content Provider Classi Java semplici, che non estendono classi del framework Android User Interface Testing 37 Testing con JUnit E’ possibile utilizzare un framework come Junit per le applicazioni Android (in particolare per le Activity)? – Problema: una sola activity può accedere attivamente all’interfaccia utente. – Soluzione: un framework di testing che esegua la activity sotto testing come sua parte, utilizzando funzionalità di instrumentazione per poterla monitorare • In questo modo, è possibile pensare di testare una Activity scrivendo dei classici Android JUnit Test Cases, nell’ambito, però, di un project separato – Da notare che il progetto di test e il progetto da testare devono avere la stessa signature (di solito si usa quella pubblica di debug) http://developer.android.com/guide/topics/testing/activity_testing.html User Interface Testing 38 Android Test Architecture User Interface Testing 39 Creazione di un progetto di test Da linea di comando si può scrivere android create test-project -m <main_path> -n <project_name> -p <test_path> In Eclipse è sufficiente utilizzare il widget di creazione progetto User Interface Testing 40 Manifest di un progetto di test <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.porfirio.orariprocida2011.test" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="4" /> Controlla l’esecuzione delle classi del package <instrumentation android:name="android.test.InstrumentationTestRunner" android:targetPackage="com.porfirio.orariprocida2011" /> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" > <uses-library android:name="android.test.runner" /> </application> </manifest> User Interface Testing 41 Creazione di una classe di test Utilizzo di JUnit3 User Interface Testing 42 Metodi generati package com.porfirio.orariprocida2011.test; import com.porfirio.orariprocida2011.OrariProcida2011Activity; import android.test.ActivityInstrumentationTestCase2; public class OrariProcida2011ActivityTests extends ActivityInstrumentationTestCase2<OrariProcida2011Activity> { public OrariProcida2011ActivityTests() { super("com.porfirio.orariprocida2011", OrariProcida2011Activity.class); } @Override protected void setUp() throws Exception { super.setUp(); } protected void tearDown() throws Exception { super.tearDown(); } } User Interface Testing 43 Primi test case Precondizioni: esistenza degli oggetti utilizzati nel test … public class OrariProcida2011ActivityTests extends ActivityInstrumentationTestCase2<OrariPr ocida2011Activity> { private OrariProcida2011Activity mActivity; private TextView mTextView1; private ListView mListView; public void testPreconditions() { assertNotNull(mTextView1); assertNotNull(mListView); } public void testTextView1(){ String resourceString = new String(mActivity.getString(com.porfir io.orariprocida2011.R.string.mezzo)); @Override protected void setUp() throws Exception { super.setUp(); mActivity = this.getActivity(); mTextView1=(TextView) mActivity.findViewById(R.id.textView1) ; mListView =(ListView) mActivity.findViewById(R.id.listMezzi) ; assertEquals(resourceString,(String)mTextView1. getText()); } protected void tearDown() throws Exception { super.tearDown(); } } } Oggetti utilizzati nel test Test sul corretto valore di una casella di testo User Interface Testing 44 Esecuzione dei test Da Eclipse Run as Junit Test Sequenza di esecuzione (Console) [2011-11-16 19:33:36 - OrariProcida2011Testing] -----------------------------[2011-11-16 19:33:36 - OrariProcida2011Testing] Android Launch! [2011-11-16 19:33:36 - OrariProcida2011Testing] adb is running normally. [2011-11-16 19:33:36 - OrariProcida2011Testing] Performing android.test.InstrumentationTestRunner JUnit launch [2011-11-16 19:33:36 - OrariProcida2011Testing] Automatic Target Mode: using existing emulator 'emulator-5554' running compatible AVD 'AVD_1_6' [2011-11-16 19:33:36 - OrariProcida2011Testing] Uploading OrariProcida2011Testing.apk onto device 'emulator-5554' [2011-11-16 19:33:36 - OrariProcida2011Testing] Installing OrariProcida2011Testing.apk... [2011-11-16 19:33:40 - OrariProcida2011Testing] Success! [2011-11-16 19:33:40 - OrariProcida2011Testing] Project dependency found, installing: OrariProcida2011 [2011-11-16 19:33:44 - OrariProcida2011] Application already deployed. No need to reinstall. [2011-11-16 19:33:44 - OrariProcida2011Testing] Launching instrumentation android.test.InstrumentationTestRunner on device emulator-5554 [2011-11-16 19:33:44 - OrariProcida2011Testing] Collecting test information [2011-11-16 19:33:48 - OrariProcida2011Testing] Sending test information to Eclipse [2011-11-16 19:33:48 - OrariProcida2011Testing] Running tests... [2011-11-16 19:34:23 - OrariProcida2011Testing] Test run finished User Interface Testing 45 Un test più complesso Esempio di test che interagisce con l’interfaccia utente, settando un valore di uno Spinner public void testListaVuota(){ mActivity.runOnUiThread( new Runnable() { public void run() { mSpnPortoPartenza.setSelection(1); mSpnPortoArrivo.setSelection(1); assertEquals(0,(int)mListView.getCount()); } }); mInstrumentation.waitForIdleSync(); } Per settare un campo di una oggetto sulla UI, è necessario, in Android, farlo interagendo nello stesso thread della UI stessa Per impedire la concorrenza tra eventi dell’utente reale ed eventi simulati da test setActivityInitialTouchMode(false); User Interface Testing 46 Activity Testing Finora sono stati esemplificati casi di test riguardanti l’interazione con la UI in un’Activity Altri possibili test da eseguire su di una Activity: – Testing in risposta a eventi riguardanti il ciclo di vita dell’Activity – Testing in risposta ad eventi di sistema – Testing in risposta ad eventi provenienti da sensori User Interface Testing 47 Testing del ciclo di vita di un Activity Per simulare una pausa e resume, ad esempio: Instrumentation mInstr = this.getInstrumentation(); mInstr.callActivityOnPause(mActivity); mInstr.callActivityOnResume(mActivity); User Interface Testing 48 Testing in isolamento Per realizzare Unit Testing è necessario limitare al minimo e controllare le dipendenze dell’unità testata dal resto del software e dell’ambiente di esecuzione La classe IsolatedContext è in grado di riprodurre un contesto di esecuzione fittizio, da utilizzare tutte le volte che sia necessario, senza dipendere dal reale stato del sistema Per emulare gli eventi di sistema si possono utilizzare le classi del package android.hardware – Per emulare I sensori si possono usare le classi Sensor, SensorEvent, SensorEventListener e SensorManager, ridefinendole in modo che generino eventi fittizi User Interface Testing 49 Esempio Mock SMSReceiver è un Broadcast Receiver che ascolta per la ricezione di SMS SMS è una Activity che viene avviata da SMSReceiver in seguito alla ricezione di un SMS e ne visualizza il testo MockProvider è una classe che estende Thread e, tramite Intent si dichiara (tramite Intent) in grado di inviare SMS e controllarne il delivery SMSMock è una classe che estende SMS, imitandolo. In pratica definisce e avvia un MockProvider SMSTesting è una classe di test che, così come SMSMock, definisce e avvia un MockProvider e gli chiede di inviare un messaggio, come prova User Interface Testing 50 Class Diagram SMSTesting istanzia SMSMock e MockProvider SMSMock sostituisce il metodo onCreate di SMSActivity e, istanziandosi, avvia MockProvider e lo dichiara come gestore degli SMS (al posto di un reale fornitore) MockProvider dichiara due Intent corrispondenti a eventi di Invio e Consegna del messaggio SMSTesting chiede a MockProvider di “inviare” un messaggio SMSReceiver riceve i messaggi fittiziamente inviati da MockProvider e li gestisce come se fossero reali <<Activity>> SMSActivity <<Broadcast Receiver>> SMSReceiver +onCreate() +onReceive() <<Thread>> MockProvider SMSMock +onCreate() 1 +sendMessage() 1 SMSTesting +test() User Interface Testing 51 Codice ... public class SmsTesting extends ActivityInstrumentationTestCase2<SMSMock> { private SMSMock myActivity; private MockProvider mymockprov; ... @Override protected void setUp() throws Exception{ super.setUp(); setActivityInitialTouchMode(false); mymockprov = new MockProvider(SmsManager.getDefault(),getInstrumentation().getContext()); } public void testcase1(){mymockprov.invia_messaggio(phoneNumber,messaggio);} ... public class MockProvider extends Thread{ private Context ctx; private SmsManager sms; private PendingIntent sentPI; private PendingIntent deliveredPI; ... @Override public void run() { sentPI = PendingIntent.getBroadcast(ctx, 0, new Intent(SENT), 0); deliveredPI = PendingIntent.getBroadcast(ctx, 0, new Intent(DELIVERED), 0); } public void invia_messaggio(String PHNUM, String MEX){sms.sendTextMessage(PHNUM, null,MEX, sentPI, deliveredPI);} } User Interface Testing 52 Unit Testing di altri componenti Per testare il ciclo di vita di un Service si possono utilizzare i metodi Context.startService e Context.bindService – Il testing di un servizio è più semplice del testing di una Activity, perchè non dipende da eventi utente e di sistema Un Broadcast Receiver è molto semplice da testare. Per avviarlo da test si può utilizzare il metodo Context.sendBroadcast per simulare l’invio di un Intent Un ContentProvider fornisce un’astrazione di accesso ai dati. Deve essere testato rispetto all’interfaccia di accesso che fornisce Alcune apposite classi da cui ereditare – ServiceTestCase, ProviderTestCase2 User Interface Testing 53 Esempi di testing Android • Alcuni esempi svolti di testing di applicazioni Android – – – – – – – – – – http://code.google.com/p/testcacciaaltesoro/ http://code.google.com/p/accelerometersensortesting/ http://code.google.com/p/alarmservicetesting/ http://code.google.com/p/brightnesssensortesting/ http://code.google.com/p/compasssensortesting/ http://code.google.com/p/giroscopioserviziotesting/ http://code.google.com/p/pressuresensortesting/ http://code.google.com/p/proximitysensortesting/ http://code.google.com/p/smsservicetesting/ http://code.google.com/p/temperaturesrvicetesting/ User Interface Testing 54 Livelli di Testing Dopo lo Unit Testing: – Integration Testing • Le varie unità sono testate in insiemi più grandi – System Testing in tre stage • Simulation stage – Il software viene testate in isolamento in un ambiente completamente simulato • Prototyping stage – L’ambiente reale inizia a rimpiazzare quello simulato, ma il software è eseguito sempre su di un emulatore • Pre-production stage – Il software è eseguito su di un device reale in un ambiente reale User Interface Testing 55 DDMS Il DDMS (Dalvik Debug Monitor Server) è uno strumento dell’Android SDK particolarmente utile in fase di prototyping – Monitora il comportamento della macchina virtuale • • • • Accesso al file system Thread in esecuzione Allocazione della memoria Log dei messaggi – Consente l’emulazione (spoofing) di • Variazioni nelle coordinate GPS • Ricezioni di messaggi SMS • Ricezione di telefonate User Interface Testing 56 DDMS User Interface Testing 57 Robotium Robotium è un framework a supporto del testing di unità delle Activity che estende e potenzia Junit. In particolare: – è più semplice scrivere test che riguardano più Activity, Dialog, Toast, Menu e Context Menu. – E’ migliorata la leggibilità dei test case – I test case sono meno dipendenti dalla variabilità dei tempi di esecuzione Robotium è un progetto open source la cuin prima versione è stata rilasciata a gennaio 2010 – http://code.google.com/p/robotium/ User Interface Testing 58 Caratteristiche di Robotium Il funzionamento di Robotium è tutto basato sull’utilizzo di un oggetto denominato SOLO Solo solo = new Solo(getInstrumentation(),getActivity()); Tramite l’oggetto solo è possibile interrogare e modificare i widget della UI, eventualmente anche senza conoscerne l’identificativo – Particolarmente utile nei test di accettazione – Ad esempio, è possibile selezionare l’insieme dei widget visibili oppure è possibile selezionare un widget in base al testo che mostra User Interface Testing 59 Esempi public void testTextView(){ String resourceString = new String(solo.getString(com.porfirio.orariprocida2011.R.string.me zzo)); TextView mTextView1=solo.getText(1); assertEquals(resourceString,(String)mTextView1.getText()); } public void testButtonRobotium(){ TextView mTxtOrario=solo.getText(3); String initial=new String(mTxtOrario.getText().toString()); solo.clickOnButton("<<"); solo.clickOnButton(">>"); assertEquals(mTxtOrario.getText().toString(),initial); } User Interface Testing 60 Monkey Monkey è un’utility interna fornita con l’android SDK, che è in grado di generare eventi utente pseudocasuali su una qualsiasi interfaccia, registrando gli eventuali crash – Monkey gira all’interno del dispositivo; per avviarla bisogna passare per adb. Ad esempio, da linea di comando: adb shell monkey –v -p com.porfirio.orariprocida2011 30 User Interface Testing 61 Output di Monkey :Monkey: seed=0 count=30 :AllowPackage: com.porfirio.orariprocida2011 :IncludeCategory: android.intent.category.LAUNCHER :IncludeCategory: android.intent.category.MONKEY // Event percentages: // 0: 15.0% // 1: 10.0% // 2: 15.0% // 3: 25.0% // 4: 15.0% // 5: 2.0% // 6: 2.0% // 7: 1.0% // 8: 15.0% :Switch: #Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10000000 ;component=com.porfirio.orariprocida2011/.OrariProcida2011Activity;end // Allowing start of Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.porfirio.orariprocida2011/.OrariProcida2011Activity } in package com.porfirio.orariprocida2011 :Sending Pointer ACTION_MOVE x=-4.0 y=2.0 :Sending Pointer ACTION_UP x=0.0 y=0.0 :Sending Pointer ACTION_DOWN x=47.0 y=122.0 Events injected: 30 :Dropped: keys=0 pointers=0 trackballs=0 flips=0 ## Network stats: elapsed time=7766ms (7766ms mobile, 0ms wifi, 0ms not connecte d) // Monkey finished User Interface Testing 62 Monkeyrunner Monkeyrunner, a differenza di monkey, è un API che consennta la scrittura di programmi in grado di controllare un dispositivo Android dall’esterno – Ad esempio è possibile scrivere un programma Python che installa un’applicazione, esegue casi di test, invia eventi, salva screenshot Esempio di programma: from com.android.monkeyrunner import MonkeyRunner, MonkeyDevice device = MonkeyRunner.waitForConnection() device.installPackage('myproject/bin/MyApplication.apk') package = 'com.example.android.myapplication' activity = 'com.example.android.myapplication.MainActivity' runComponent = package + '/' + activity device.startActivity(component=runComponent) device.press('KEYCODE_MENU', MonkeyDevice.DOWN_AND_UP) result = device.takeSnapshot() result.writeToFile('myproject/shot1.png','png') User Interface Testing 63 Android Crawler Una alternativa, con più “intelligenza” di Monkey è l’Android Crawler sviluppato all’Università di Napoli – Navigazione “in ampiezza” dell’interfaccia utente di un’applicazione Android • Esecuzione di eventi su tutti i Widget trovati – – – – Generazione di sequenze di esecuzione Generazione di casi di test JUnit Rilevazione automatica di crash Valutazione della copertura ottenuta (con Emma) User Interface Testing 64 Problemi L’albero è teoricamente illimitato Si può limitarlo: – fissando un limite alla profondità – interrompendo l’esplorazione quando si giunge ad una Activity “simile” ad una già esplorata User Interface Testing 65 Emma Emma è uno strumento di istrumentazione del codice sorgente che consente di valutare l’effettiva copertura del codice ottenuta a seguito dell’esecuzione di un insieme di casi di test Può essere eseguito solo da linea di comando, tramite adb. Ad esempio: – adb shell am instrument -w -e coverage true [Package di test]/android.test.InstrumentationTestRunner Genera report e metriche, anche in formato HTML Molto utile per il testing White Box User Interface Testing 66 Output di EMMA User Interface Testing 67 Copertura del codice con EMMA User Interface Testing 68 Altri processi di testing Performance testing Stress testing Security Testing Compatibility Testing Usability Testing Accessibility Testing User Interface Testing 69 Appendice: Analisi delle vulnerabilità di applicativi webbased e studio delle contromisure User Interface Testing 70 Sql injection Tecnica volta a sfruttare cattive implementazioni al fine di creare query “maligne” Inserisco : Login Password qualsiasi 1' or '1' = '1 User Interface Testing 71 Studio query ResultSet rs = statement.executeQuery("SELECT * FROM operatore2 " +"WHERE login_op='" +login+ "' AND " +"password_op='" +pass+ "'"); Query risultante dalla sostituzione dei parametri SELECT * FROM operatore2 WHERE login_op='qualsiasi' AND password_op='1' or '1' = '1‘. Il confronto 1=1 è sempre vero ed è per questo che come risultato avremo tutte i record del database. User Interface Testing 72 Rimedi Sql injection Utilizzo dei prepared statement PreparedStatement stmt= conn.prepareStatement ("SELECT * FROM operatoremd5 WHERE login_op= ? AND password_op= ?"); stmt.setString(1,login); stmt.setString(2,password); ResultSet rs= stmt.executeQuery(); Parametrizazione dei dati Evitata la concatenazione di stringhe Più efficienza nel caso di una stessa query richiamata con parametri diversi User Interface Testing 73 Validazione degli input Il tipo di dato (string, integer, real, etc…) Il set di caratteri consentito La lunghezza minima e massima dei caratteri Controllare se è permesso il tipo NULL Controllare se il parametro è richiesto o meno Intervallo numerico Esempi di codice per i controlli Lunghezza if(login.length()>15 || pass.length()>15) Campi vuoti if (pass.length()==0 || login.length()==0) Tipo di dato Blocco try{ } catch (NumberFormatException e){codice per la gestione} Generazione null point exception Blocco try{ } catch (NullPointerException e){codice per la gestione} Nel caso uno di questi controlli non fosse rispettato l’utente deve essere informato da un messaggio di errore User Interface Testing 74 Insecure Storage Codifica dei dati per renderli illeggibili Algoritmi: Md5 Rsa Md5 Non è invertibile Generazione di Stringhe univoche a partire dall’input. Non crea ambiguità User Interface Testing 75 Escalation di permessi Si tenta di accedere a funzionalità non autorizzate al ruolo.Nella prima figura un semplice utente pùo relizzzare solo la funzione di visualizzazione profili.Nella seconda l’amministratore ha a disposizione anche quella di eliminazione. Dati inviati da un semplice utente per realizzare la funzione di visualizzazione Dati inviati dall’amministratore per realizzare la funzione di visualizzazione User Interface Testing 76 Studio della pagina funzioni.jsp int opr = Integer.parseInt(request.getParameter("ruoloOp")); botton1 = request.getParameter("visProfilo"); if(botton1!=null){ if (botton1.equalsIgnoreCase("Visualizza profilo") && opr == 3) { //codice per effettuare la funzione visualizza profilo //} String del = request.getParameter("elimina"); else if (del.equalsIgnoreCase("elimina profilo") && (opr == 3)) {// codice per l’eliminazione utente //} Recupero dei dati mostrati in precedenza per conoscere la scelta selezionata Controllo del ruolo dell’utente che richiede la funzionalità, effettuato mediante un hidden field Esecuzione della funzionalità prescelta User Interface Testing 77 Esecuzione dell’escalation di permessi Da utente normali richiesta della funzione di visualizzazione sull’utente valentino Intercettiamo i dati con Web Scarab 1) 2) Modifica del ruolo utente Modifica del tipo della seconda coppia di Variabili Valore Notifica di avvenuta eliminazione confermata dalla stampa degli utenti ancora presenti nell’applicativo. User Interface Testing 78 Gestione corretta delle funzioni Recupero del ruolo utente memorizzato ,in fase di autenticazione,nell’oggetto session (lato server) ruoloop=((beans.Operatore)session.getAttribute("operatore")).getRuolo(); Aggiunta di un controllo che appura l’utente che sta richiedendo la specifica funzione. if(vis.equalsIgnoreCase("Visualizza profilo") && db.èautorizzato(ruoloop,"Visualizza profilo")) { codice per eseguire la funzione visualizza } if(elimina.equalsIgnoreCase("Elimina operatore")&& db.èautorizzato(ruoloop,"Elimina operatore")){codice per eseguire la funzione elimina } La funzione èautorizzato in base al ruolo dell’utente e al tipo di funzione da eseguire restituisce l’atorizzazione a processarla.Il codice è il seguente: public boolean èautorizzato(int ruoll,String funzione){ if(funzione.equalsIgnoreCase("Elimina operatore")) return (ruoll==3); if(funzione.equalsIgnoreCase("Visualizza profilo")) return true; else return false; } Provando ora a richiedere la funzione di eliminazione da semplici utenti User Interface Testing 79 Accesso a pagine riservate Non prevedendo meccanismi di controllo chiunque può accedere ad una pagina anche se non consentito dal ruolo. Sicurezza basata sul Security through Obscurity User Interface Testing 80 Corretta policy di accesso alle pagine Ad ogni pagina viene associato un livello di protezione Viene richiamata una funzione che verifica se l’utente possiede i permessi per fruire della pagina. Nel caso non si possiedono i permessi si viene rimandati alla pagina principale con un messaggio di errore User Interface Testing 81 XSS Cross-site scripting Immissione di codice javaScript non opportunamente filtrato allo scopo di sottrare dati sensibili agli utenti (cookie,password..) Stored:codice immesso rimane in maniera permanente nell’applicativo es. immissione come post di un blog XSS Reflected:codice è valido solo per una richiesta specifica es.inserimento in un campo di ricerca User Interface Testing 82 Simulazione stored xss •Immissione nel campo mail del codice: <script>alert("Il tuo session ID è stato rubato ed è:"+document.cookie)</script> •Il codice ruba il session id di chi visualizza il profilo “maligno” User Interface Testing 83 Simulazione stored xss L’amministratore una volta loggato visualizza il nuovo profilo L’utente “hacker” riceve il session id dell’amministratore User Interface Testing 84 Simulazione stored xss Recuperato il session id l’utente effettua una richiesta ad una pagina accessibile solo all’amministratore modificando il suo header Verremo riconosciuti come amministratori potendo accedere a tutte le funzionalità User Interface Testing 85 Rimedi contro l’Xss Codifica dei dati in html in particolare dei caratteri speciali Metodo encode della classe HtmlEncoder Il codice immesso risulterà: <script>alert("Il tuo session ID è stato rubato ed è:"+document.cookie)</script> Il browser non interpreterà più il codice come JavaScript ma come semplice testo rendendolo innocuo User Interface Testing 86 Conclusioni Alcuni principi da seguire per garantire la sicurezza: Principio del Secure by default (password complesse ,rinnovo periodico) Principio del Least Privilege Principio del Defense in depth Considerare i sistemi esterni come insicuri Non fidarsi del “ Security through Obscurity ” Prendere le giuste contromisure Considerare la sicurezza come un processo e trattarlo come tale. Esso deve partire già dalla fase di progettazione nonchè prevedere opportuni penetration testing User Interface Testing 87