Menu
Menu
Contattaci
Il nostro settore richiede uno studio continuo e una forte attenzione all’innovazione. Incentiviamo quindi dei programmi formativi annuali per tutti i componenti del team, con ore dedicate (durante l’orario di lavoro) e serate formative sia online che in presenza. Sponsorizziamo eventi, sia come partner che semplicemente come partecipanti, e scriviamo articoli su quello che abbiamo imparato per essere, a nostra volta, dei divulgatori.
Vai alla sezione TeamNell’intraprendere una nuova collaborazione, vediamo che le principali motivazioni del cliente sono correlate alla risoluzione di un problema o alla soddisfazione di un bisogno. Noi, in quanto Team di Sviluppo, ci prodighiamo a capire le cause che spingono il cliente ad identificare la sua situazione attuale come bisogno o problema ed intraprendiamo con lui quella strada lunga e tortuosa, che si conclude nello stilare i requisiti funzionali che la nostra soluzione (per noi tipicamente software) dovrà garantire. Essendo questo un processo complesso, ed essendo il cliente spesso audace ma al contempo indeciso, può capitare che quello che abbiamo implementato per lui, a partire dai requisiti funzionali, non sia esattamente quello che desiderava. Tecniche Agile come Scrum e Kanban possono ridurre al minimo questo tipo di problematica, garantendo demo e rilasci molto frequenti in modo da cambiare la rotta di sviluppo in un tempo accettabile, tuttavia in questo articolo affronteremo una tecnica che ci aiuta a centrare il desiderio del cliente (da qui in avanti anche detto Team di Business) tramite la definizione di criteri di accettazione (Acceptance Criteria, AC) confermati dagli User Acceptance Test (UAT). Abbiamo dunque bisogno della definizione degli UAT per possedere una metodologia di test formale, definire gli AC e centrare i desideri del cliente, piuttosto che basare il nostro sviluppo esclusivamente su una documentazione ricevuta ad inizio progetto. Se la soluzione software si rivelasse essere di grosse dimensioni e durata di sviluppo, inoltre, i Business Requirements (BR) del cliente potrebbero variare in corso d’opera rispetto a quelli inizialmente stabiliti.
Non entreremo nel dettaglio della definizione degli UAT, dato che è un argomento che riguarda principalmente il Team di Business, tuttavia vedremo delle tecniche che, se applicate durante la fase di sviluppo, possono supportare in modo non indifferente i test di accettazione.
Una caratteristica particolare degli UAT è che non coinvolgono solo sviluppatore e cliente, bensì l’intero Team di Sviluppo e Team di Business, poiché vanno a toccare l’intero business domain, o per così dire l’intera Supply Chain del client, se questo ne possiede una. Gli stakeholders dunque sono:
Queste figure prenderanno parte alla definizione ed all’esecuzione degli UAT, al fine di cercare il più possibile di evitare le sorti del Chaos Report.
Il Chaos Report è uno studio portato avanti da un gruppo di professionisti dell’ICT chiamato The Standish Group, con lo scopo di collezionare statistiche di casi reali di fallimento di soluzioni IT per migliorarne con il tempo il tasso di successo. Il primo report originale è stato pubblicato nel 1994 e fino al 2015 ne ha mantenuto la stessa forma (oggi i report collocati in quel periodo sono denominati legacy), mentre dal 2016 possiamo trovare un nuovo formato del report. La versione del 1994 esaminò 8380 casi e rilascio le seguenti percentuali:
Il report inoltre prova ad indentificare le cause dei suddetti fallimenti stilando un elenco ordinato secondo probabilità:
Molte di queste cause possono essere coperte eseguendo uno UAT formare, come vedremo di seguito.
Per definire dei test di accettazione, dobbiamo gioco forza capire quali sono i criteri da testare, tipicamente definiti dal Team di Business. Un Acceptance Criteria (AC) è dunque la regola o il contratto che una funzionalità, un componente o un sistema deve rispettare affinché venga accettato dal Business. Se i BR ci dicono cosa il nostro sistema deve fare, gli AC ci danno l’idea dello stato dello sviluppo e ci fanno capire se il sistema è pronto o meno per il rilascio. Ovviamente tutto dipende dalla qualità degli AC; un criterio troppo generico rischia di coprire pochi requisiti funzionali e non rispecchiare precisamente i bisogni del Business, oltre a portare ulteriori danni, come non rientrare in costi e tempi o testare funzionalità non ben definite.
Inoltre gli AC hanno un ulteriore beneficio: se alla data di rilascio prevista tutti gli AC non sono stati rispettati, questi possono essere usati per dare una stima del lavoro mancante e della quantità di tempo ulteriore da investire nello sviluppo.
Per gli AC come per i BR, tipicamente la causa principale di inesattezze, errori o forti discostamenti dalle definizioni originali, è la sostanziale differenza di conoscenza verticale fra i vari stakeholders del progetto: sponsor, manager, sviluppatori ed utenti finali hanno tutti descritto cosa il sistema deve fare; tuttavia lo sponsor magari conosce gli ingranaggi del business ma non come funziona tecnicamente, utente finale e manager conoscono cosa il sistema deve fare ma spesso non come funzionerà, mentre lo sviluppatore capisce la tecnologia del sistema ma non il business che c’è dietro.
Quali sono, dunque, i test di accettazione che Team di Sviluppo e Team di Business devono coprire? Sicuramente tutti quelli scaturiti da BR definiti nel contratto firmato fra le parti, insieme a quelli che coprono le aspettative dell’utente; inoltre, dove c’è logica di business ci sono fisiologicamente dei Business Processes (BP), che dovrebbero essere tutti coperti da test. Ci dovrebbe essere, infine, un Test detto “di regressione“, per ogni cambiamento del sistema in corso d’opera, al fine di evitare i fastidiosissimi bug di regressione. Tutto quello che è stato già testato in fase di sviluppo, come unit test, integration test, system test, non dovrà essere coperto da UAT.
Un UAT, per essere considerato tale, deve essere formale, oggettivo e strutturato; la prima caratteristica permette al test di verificare se una funzionalità o un sistema rispettano i requisiti richiesti, ovvero rispettano quello che è diventato lo “standard” per il cliente; inoltre un approccio strutturato permette di garantire che ogni funzionalità abbia almeno un test che la copra. Infine deve essere oggettivo in quanto, se durante la sua esecuzione vengono evidenziate delle problematiche o delle mancanze, deve provvedere ad esporne le prove concrete, in modo da rendere più veloce ed accurato il debugging e la risoluzione. In ambito UAT, infatti, il testing ed il bug fixing sono due attività fortemente disaccoppiate, al contrario di quanto accade normalmente, in quanto il Team di Business, ed in particolare l’utente finale, è tenuto a trovare i bug ma non ad eseguirne un fix, mentre il Team di Sviluppo può risolvere l’errore ma non portare avanti il test.
Come accennato in precedenza, gli UAT sono l’ultima classe di test ad essere eseguita e non comprendono nessuna, o quasi, delle altre classi di test, come unit, integration, etc. Diciamo quasi perché ci sono in realtà due classi di test di cui l’UAT fa uso: il Functional Testing (detto anche Black-Box Testing) e lo Structural Testing (detto anche White-Box Testing).
In fin dei conti in fase di test dobbiamo essere in grado di completare gli UAT nel modo più efficace e efficiente possibile. Per fare ciò la letteratura ci da una mano fornendoci due processi tipici di UAT, il Fundamental Test Process (FTP) ed il Test Development Process (TDP).
L’FTP si struttura in cinque passi e fornisce la sequenza di attività da compiere per completare il test e degli indici per stabilire il successo o meno di quest’ultimo. I cinque passi sono:
Come è facilmente intuibile, i White-Box e Black-Box tests aiutano a coprire molti di questi punti.
Il TDP invece descrive le meccaniche di generazione di test effettivi che aiutano a raggiungere la copertura dei test all’interno del componente o sistema desiderati negli AC. Questo si compone di tre parti:
Una delle fasi più importanti degli UAT è il design dei Test-Cases, tramite cui, come anticipato, i tester inizieranno e porteranno avanti il loro lavoro. Dei buoni Test-Cases garantiranno efficienza ed efficacia. La letteratura ci suggerisce due principali tecniche di design[3] che in realtà sono molto simili a design pattern, ovvero soluzioni note per problemi ricorrenti:
L’applicazione di questi due pattern ci permette di avere a disposizione un buon papier di Test-Cases da consegnare a chi di dovere, ovvero i testers facenti parte del Team di Business.
Infine, insieme alle tecniche di design, un corretto approccio ai test è fondamentale per la loro buona riuscita. La ricca scelta di approcci deriva dal fatto che, alla fine dei giochi, sono gli utenti finali a decretare gli AC, e che i BR e i BP sono spesso incompleti ed a volte errati. Gli utenti finali dunque, avendo esperienze “orizzontali” assai diverse, sono gli unici ad essere davvero oggettivi nei test, in quanto si distaccano da quella conoscenza ed esperienza verticale posseduta dai tecnici che può invalidare il risultato del test;
Ci sono molti approcci, ma i più famosi sono i seguenti (che si basano sugli aspetti fondamentali degli uat): BR, BP e AC, in cui tutti vanno a generare Test-Cases:
Dunque dopo aver ottenuto un forbito bagaglio di Test-Cases, il Team di Business potrà iniziare a testare le funzionalità ed i moduli del sistema da noi sviluppato. E a questo proposito, noi sviluppatori? Siamo una mera parte passiva nell’ambito degli UAT?
Proseguendo vedremo come una tecnica ed un meccanismo possano decorare e completare gli UAT al fine di supportare l’intero Team di Business, oltre che portare dei grossi vantaggi allo sviluppo.
Una delle metodologie per lo sviluppo dei test di accettazione, associata alla famosa tecnica del TDD, è quella del Outside-In; nata in quel di Londra (detta infatti anche London Style o Double Loop), reclama il fatto che i test siano in grado di definire una sorta di contratto fra il team di sviluppo, capitanato dal Product Owner, ed il Team di Business; sempre che quest’ultimo sia abbastanza intraprendente da essere disposto a leggere qualche riga di codice auto esplicativo.
Questa tecnica presuppone la forma mentis “Outside-In”, ovvero stabilisce che per definire il codice assieme ai test tramite TDD, si debba partire dagli strati più esterni della nostra infrastruttura software, cosa che invece viene lasciata libera nello stile “Classico” di TDD.
Se in un’architettura ben testata abbiamo Test di interfaccia, Unit test, Integration Test e UaT, nell’Outside-In iniziamo definendo gli UaT a partire dai requisiti discussi con il team di Business e supportati da tecniche di mocking, andremo ad eseguire il cosiddetto Double Loop, composto da loop dell’UAT e da n loop dei relativi Unit Test. Come mostrato nella figura seguente, ragioniamo di loop di fallimento e non di sviluppo, in quanto, supponendo di trovarci all’i-esimo ciclo di iterazione, la metodologia prevede di:
Vediamo un esempio:
Supponiamo di riuscire a scrivere un criterio di accettazione per un requisito stabilito fra il team di sviluppo ed il team di business, il tutto in un linguaggio facilmente comprensibile che usa i prefissi Given-When-Then. Potremmo trovarci davanti ad una situazione simile a quella descritta dalla figura successiva.
Tramite la tecnica di ATDD (Acceptance Test Driven Developement) possiamo trasformare i suddetti requisiti in metodi che fanno da proxy ai nostri servizi, in questo modo.
Come possiamo vedere, il nostro IDE ci aiuta nel rifattorizzare il metodo appena chiamato. Se volessimo fare build del progetto e far eseguire il test, otterremmo banalmente un lancio dell’eccezione NotImplementedException(), quindi possiamo andare avanti ed iniziare a chiamare il servizio “Under Test”. In ATDD vengono esclusi i test di interfaccia, dunque i primi strati ad essere toccati sono quelli mediatori fra interfaccia e logica, come i controller in MVC oppure i ViewModel in MVVM. Nel nostro caso definiamo un ViewModel che gestirà la logica dell’aggiunta del device, con il supporto dei servizi mocked.
Vediamo che l’IDE, nell’aiutarci nel refactor e nella creazione automatica delle classi e dei metodi, ci metterà sempre i paletti di mancata implementazione, che saranno delle piccole milestone verso cui dovremo arrivare per evitare di far fallire il test. In questo momento siamo ancora a livello Outside, infatti se runniamo lo UAT lo vedremo fallire.
A questo punto potremo iniziare l’implementazione del nostro metodo concreto, che farà uno di alcuni servizi dedicati: grazie all’introduzione di due servizi mocked, non ci preoccuperemo inizialmente della loro implementazione e, rispettando la loro interfaccia, li passeremo al nostro view model tramite dependency injection.
Lo unit test seguente fa uso dei servizi mocked, chiama il metodo in test del viewModel e esegue una verify, ovvero verifica che i metodi definiti nel setup del mock siano effettivamente stati chiamati con quel tipo di parametri in ingresso:
Avendo concluso le iterazioni dell’”In”, potremo rieseguire il test estero, ovvero lo UAT e vederlo completare; potremo così continuare le nostre iterazioni fino a coprire i Given-When-Then dati dai requisiti funzionali.
Un modo che abbiamo come sviluppatori per supportare gli UAT del team e per completare il giro di test della nostra architettura è l‘utilizzo di test End-To-End (E2E) automatici. Prendendo in esempio il caso delle applicazioni mobile in Xamarin, possiamo utilizzare il framework di test Xamarin.UITest, che permette di eseguire automaticamente test di interfaccia su applicazioni Xamarin.Android e Xamarin.IOS, sfruttando il ben più noto Framework NUnit. I test scritti in Xamarin.UITest interagiscono con l’interfaccia grafica dell’applicazione, simulando l’interazione dell’utente tramite tap, gesture, inserimenti di testo etc.
Dopo l’integrazione di Xamarin nel mondo Microsoft, il framework di test compie un passo in più, ovvero è possibile pubblicare i test sviluppati e testati in locale su App Center, insieme agli apk/ipa delle nostre applicazioni; in questo modo potranno essere eseguiti in cloud su tutta una serie di device diversi fra loro, ovviamente a pagamento.
Per scrivere gli UITest in Xamarin, Microsoft consiglia una metodologia a Loop attuata durante lo sviluppo delle nostre app, ovvero:
Simulando come anticipato l’interazione dell’utente con il device, gli UITest sono strettamente legati alle viste ed agli elementi grafici che si trovano sullo schermo. Per questo fanno uso di due specifici Set di API che si interfacciano l’un l’altro:
Il tutto basandosi sul paradigma Read-Eval-Print-Loop (REPL). Gli UITest fanno uso dell’interfaccia IApp che definisce le firme dei metodi utilizzati dalle sue implementazioni iOSApp e AndroidApp tramite i quali si interfaccia all’applicazione. Microsoft suggerisce di creare una nuova istanza di IApp per ogni test. Per fare ciò ne creiamo una all’interno del metodo in override SetUp.
Come possiamo vedere, la creazione dell’istanza è di tipo Builder, in cui dobbiamo specificare la device platform (Android o iOS) e il path (assoluto o relativo) del file .apk o .app della nostra applicazione. Per poter testare un’app iOS è necessario possedere un provisioning profile collegato ad un dispositivo fisico, in quanto sul simulatore iOS non è possibile farlo. Cosa diversa avviene per Android, in cui dovremo disabilitare lo Shared Mono Runtime, questo a discapito delle performance di building e distributing della nostra app. Per fare ciò, andiamo nelle impostazioni del progetto di Android, nella sezione Android Build -> General e togliamo il flag “User Shared Mono Runtime” e dare i permessi per l’utilizzo di internet all’app tramite la stringa <uses-permission android_name=”android.permission.INTERNET” /> nel manifest.
Successivamente creiamo il metodo di UI Test per coprire la nostra funzionalità in via di sviluppo, esattamente come faremmo per uno Unit o Integration test con NUnit.
Come possiamo vedere, l’oggetto _sut (System Under Test) contiene l’implementazione di IApp e offre metodi Actions e Queries come Tap() e Query() che permettono di interagire con la UI e di richiederne lo stato, oltre a darci la possibilità di scattare screenshots.
Infine testiamo il risultato tramite una classica assert.
In questo modo, eseguendo i test da noi scritti, Xamarin.UiTest troverà l’apk (o .app in caso di iOS) dal path che gli abbiamo fornito, lo installerà nel dispositivo disponibile fisico (per iOS) o emulato ed eseguirà il test automatizzato.
Xamarin.UITest ci offre un altro paio di tecniche per ispezionare la vista, ovvero il REPL ed il Marked:
Dunque, con un approccio agile allo sviluppo del software, tramite la tecnica del ATDD ed un rinforzo agli edges della soluzione software tramite il meccanismo dello UI Testing, possiamo dare, in quanto Team di Sviluppo, un sostanziale supporto positivo agli User Acceptance Test.
Bibliografia: User Acceptance Testing, A step-by-stepguide, Hambling, Van Goethem, Cap 2-3.
Segui Giuneco sui social