• Come un abito cucito su misura, i nostri progetti software uniscono le potenzialità di tecnologie innovative alle specifiche esigenze del cliente.

    Showcase progetti
  • Analizziamo e razionalizziamo con il cliente i processi e le attività della propria azienda, per poi seguirlo nella scelta delle giuste strategie IT da adottare.

    Le nostre competenze
  • Crediamo nelle potenzialità di un percorso formativo che unisca forti basi teoriche e metodologiche ad una continua applicazione pratica di quanto si apprende.

    Dettaglio dei corsi
  • Progettiamo e sviluppiamo sistemi e progetti basati su dispositivi mobile consumer (iPhone, Windows Phone 7 Series), industriali (Windows CE) e custom (basati su microcontrollori 8/16/32 bit).

    Approfondimenti
 

Test di applicazioni Silverlight con Visual Studio Feature Pack 2

November 19, 2010 18:01 by l.maiorfi

Rilasciato qualche giorno fa, il feature pack 2 per Visual Studio 2010 contiene al suo interno diverse novità interessanti, sommariamente divise in due aree: design e testing.

Tralasciando per ora l’area design, della quale torneremo a parlare dopo un po’ di sperimentazione “sul campo”, ci concentreremo in questo post sulle nuove funzionalità relative al test di applicazioni web, in particolare per quanto concerne lo sviluppo di procedure di “Coded UI Tests” per applicazioni Silverlight. Mentre infatti per applicazioni web “pure” esistono tool quali WatiN o Selenium, in cui l’analisi delle interazioni con il browser e la valutazione delle asserzioni avviene a livello di DOM, nel caso di applicazioni Silverlight la piattaforma di test “ufficiale” non prevede niente di analogo, limitandosi ad un insieme di strumenti utili al più per lo Unit Testing dei ViewModels, strumenti in cui ci si è concentrati a nostro avviso più sulla possibilità di effettuare test asincroni (e non di funzioni asicrone) che sulle reali necessità di chi sviluppa applicazioni LOB con Silverlight.

La principale novità introdotta riguarda la possibilità di creare progetti di Test, integrati nella piattaforma di testing di Visual Studio 2010, di tipo “Coded UI Tests”, ossia in cui tramite un modello ad oggetti accessibile da codice si sviluppano metodi di test che interagiscono, per tramite del browser, con gli elementi visuali di un’interfaccia utente Silverlight. Inoltre, alla flessibilità del modello ad oggetti in questione è stato affiancato un tool di “cattura” delle interazioni con la UI che permette anche di generare asserzioni in maniera completamente visuale.

Vediamo ora un esempio, a titolo di “walkthrough”, in cui creeremo una procedura di test volta a verificare che un’applicazione Silverlight (assolutamente dimostrativa, ancorché basata sul pattern MVVM) funzioni a dovere.

L’applicazione “under test” si presenta come illustrato nell’immagine seguente. Lo scopo di tale applicazione è quello di calcolare la data della Pasqua nell’anno indicato dalla apposita TextBox.

image

Senza soffermarci troppo sull’implementazione (trovate comunque i sorgenti qui), vediamo come creare un progetto di test di tipo “Coded UI Test”.

Come prima cosa, va aggiunto ai riferimenti del progetto Silverlight l’assembly “Microsoft.VisualStudio.TestTools.UITest.Extension.SilverlightUIAutomationHelper”. Sul sito MSDN viene sottolineato che tale assembly NON E’ liberamente ridistribuibile e che va pertanto rimosso dai riferimenti prima dell’effettivo rilascio in produzione. Viene inoltre illustrato come renderne l’inclusione condizionata ad un parametro “Production” da valorizzare opportunamente al momento della compilazione del progetto con MSBuild, feature utile prevalentemente a chi fa Continuos Integration.

Occorre quindi specificare un identificativo per ciascun controllo di ciascuna vista che si intende utilizzare nei Test. Le proprietà utilizzabili sono diverse, ma in ottica MVVM direi che il candidato migliore è “AutomationId”, come mostrato nel frammento di XAML che segue, relativo alla nostra unica View:

   1: <TextBox AutomationProperties.AutomationId="TxtYear" Margin="5" Padding="5" Text="{Binding Year,Mode=TwoWay}" />
   2:  
   3: <sdk:DatePicker AutomationProperties.AutomationId="DtpEaster" SelectedDateFormat="Long" SelectedDate="{Binding DtEaster}" Margin="5"/>
   4:  
   5: <Button AutomationProperties.AutomationId="BtnCalcola" Content="Calcola Pasqua" Command="{Binding CommandComputeEaster}" Margin="5" Padding="5" />

A questo punto è possibile aggiungere alla Solution un progetto di tipo Test. Eliminato, se si vuole, il file UnitTest1.cs compreso nella confezione, è arrivato il momento di aggiungere l’agognato Item di tipo “Coded UI Test”. Al momento della inclusione apparirà una richiesta di questo tipo (successivamente potranno essere utilizzate allo stesso scopo le corrispondenti voci di menu contestuale direttamente all’interno del sorgente di un metodo di Test):

image 

La prima scelta avvierà un tool di cattura che ci consentirà di registrare tutte le azioni che effettueremo attraverso il browser sull’interfaccia utente della nostra applicazione. Il tool in questione si presenta come illustrato sotto. Sono presenti un pulsante per avviare la registrazione, un pulsante che mostra un log sintetico degli step catturati, un “mirino” per la selezione dei controlli relativi alla generazione delle asserzioni “visuali” ed un pulsante per la generazione dei sorgenti relativi al playback delle azioni catturate e delle asserzioni definite.

image

Premuto il pulsante di registrazione ed avviato il browser, questo si presenterà con la barra del titolo leggermente modificata, ad indicare la cattura in corso:

image 

Dopo aver eseguito la sequenza di Test desiderata, ad esempio nella nostra applicazioni dimostrativa specificando l’anno 2011 e cliccando sul pulsante “Calcola Pasqua”, e dopo aver generato i sorgenti relativi alle operazioni catturate, è possibile (e opportuno) generare una asserzione che controlli che il valore calcolato sia effettivamente quello atteso, mediante la funzione del tool illustrata nelle due immagini che seguono.

image 

image

 

Se tutto è andato come deve, dopo aver di nuovo dato il via alla generazione del codice del test, vedremo il seguente frammento all’interno del metodo di test editato.

   1: [TestMethod]
   2: public void CodedUITestMethod1()
   3: {
   4:     this.UIMap.CalcoloPasqua2011();
   5:     this.UIMap.AssertDataGiustaDom24Apr2011();
   6: }

In cui vengono invocati due metodi generati dal tool di cattura il cui sorgente è riportato di seguito.

   1: /// <summary>
   2: /// CalcoloPasqua2011 - Use 'CalcoloPasqua2011Params' to pass parameters into this method.
   3: /// </summary>
   4: public void CalcoloPasqua2011()
   5: {
   6:     #region Variable Declarations
   7:     WinButton uINotificationChevronButton = this.UIItemWindow.UINotificationChevronButton;
   8:     BrowserWindow uIHttplocalhost3821DemWindow = this.UIHttplocalhost3821DemWindow;
   9:     SilverlightEdit uITxtYearEdit = this.UIHttplocalhost3821DemWindow.UIHttplocalhost3821DemDocument.UISilverlightControlHoPane.UIItemCustom.UIMainPageMainPage.UIItemPage.UIPageScrollViewerPane.UITxtYearEdit;
  10:     SilverlightButton uICalcolaPasquaButton = this.UIHttplocalhost3821DemWindow.UIHttplocalhost3821DemDocument.UISilverlightControlHoPane.UIItemCustom.UIMainPageMainPage.UIItemPage.UIPageScrollViewerPane.UICalcolaPasquaButton;
  11:     #endregion
  12:  
  13:     // Go to web page 'about:blank' using new browser instance
  14:     this.UIHttplocalhost3821DemWindow.LaunchUrl(new System.Uri(this.CalcoloPasqua2011Params.UIHttplocalhost3821DemWindowUrl));
  15:  
  16:     // Go to web page 'http://localhost:3821/DemoSLTestingTestPage.aspx'
  17:     uIHttplocalhost3821DemWindow.NavigateToUrl(new System.Uri(this.CalcoloPasqua2011Params.UIHttplocalhost3821DemWindowUrl1));
  18:  
  19:     // Type '2011' in 'TxtYear' text box
  20:     uITxtYearEdit.Text = this.CalcoloPasqua2011Params.UITxtYearEditText;
  21:  
  22:     // Click 'Calcola Pasqua' button
  23:     Mouse.Click(uICalcolaPasquaButton, new Point(48, 10));
  24: }
  25:  
  26: /// <summary>
  27: /// AssertDataGiustaDom24Apr2011 - Use 'AssertDataGiustaDom24Apr2011ExpectedValues' to pass parameters into this method.
  28: /// </summary>
  29: public void AssertDataGiustaDom24Apr2011()
  30: {
  31:     #region Variable Declarations
  32:     SilverlightEdit uITextBoxEdit = this.UIHttplocalhost3821DemWindow.UIHttplocalhost3821DemDocument.UISilverlightControlHoPane.UIItemCustom.UIMainPageMainPage.UIItemPage.UIDtpEasterDatePicker.UITextBoxEdit;
  33:     #endregion
  34:  
  35:     // Verify that 'TextBox' text box's property 'Text' equals 'domenica 24 aprile 2011'
  36:     Assert.AreEqual(this.AssertDataGiustaDom24Apr2011ExpectedValues.UITextBoxEditText, uITextBoxEdit.Text);
  37: }

Da questo momento in poi, è possibile eseguire il Test in questione come qualsiasi altro, ad esempio avviando la verifica dalla ToolWindow “TestView”:

image

L’esecuzione del test aprirà il browser, eseguirà i passi registrati (apparentemente non con gli stessi tempi, pur rispettando la logica degli stati dei controlli con cui c’è interazione) e verificherà le asserzioni, riportando l’esito del test come sempre nella ToolWindos “TestResults” di Visual Studio:

image

Trovate i sorgenti relativi al progetto di esempio qui.


Currently rated 5.0 by 2 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Test della UI di una applicazione con Selenium

September 4, 2009 15:17 by F.Bernabei

Al giorno d’oggi predisporre dei test automatici sulle applicazioni che sviluppiamo è divenuto un requisito fondamentale per vari motivi. Primo fra tutti per evitare che a seguito di modifiche al software (per introdurre nuove funzionalità o correzione di bug) si possa in modo automatico, verificare che tutte le funzionalità precedenti (o meglio tutte quelle precedentemente messe sotto test e verificate) continuino a funzionare in modo corretto e che non si siano quindi introdotti ulteriori bug con le modifiche apportate (questi sono detti test di [non] regressione).

In questo articolo parleremo in particolare di test automatizzati dell’interfaccia utente, argomento che ho già introdotto in un mio post precedente pubblicato su DotNetUmbria.org a cui si può far riferimento per un’introduzione di base.
A differenza degli unit test, l’automatizzazione della UI permette di fare controlli ad un “livello più alto” ed integrato, utile per testare in modo ripetitivo un intero caso d’uso della nostra applicazione o aggiungere un set di test completo ad applicazioni già esistenti.
Come illustrato nell’articolo introduttivo appena menzionato, esistono vari framework per raggiungere il nostro scopo  e sicuramente due dei più utilizzati sono WatIn e SelniumHQ (di cui parleremo in modo approfondito nel prosegui dell’articolo). Si tratta di framework che permettono il test di applicazioni WEB come vedremo in seguito, ma va segnalato che esistono soluzioni analoghe anche per il test di applicazioni Windows, come l’ottima Microsoft UI Automation  per il test di applicazioni Windows Presentation Foundation che magari potrebbe essere argomento per un articolo futuro.

Ma torniamo allo scopo principale di questo post, ossia vedere un esempio concreto di implementazione di un test di un’applicazione reale implementato mediante l’uso di Selenium. Come caso d’uso per provare le funzionalità di base del framework ho scritto una piccola applicazione ASP.NET che banalmente permette di inserire del testo in una casella di testo ed alla pressione di un bottone riporta il testo inserito in una etichetta. Il risultato finale in esecuzione è visibile nell’immagine seguente ed il codice è scaricabile direttamente dal link presente al termine dell’articolo.

uitestwebapp

Come si evince dall’immagine in realtà ci sono due bottoni che eseguono l’operazione appena descritta. Questo per poter provare diversi approcci di test in quanto in un caso l’aggiornamento dell’etichetta viene fatto con codice lato server ed il bottone esegue quindi un postback (all’interno di un UpdatePanel Ajax) e nel codice C# della pagina viene aggiornato il testo.
Nel secondo caso invece il lavoro viene eseguito completamente lato client con del codice Javascript, facendo uso anche del comando alert per visualizzare una finestra di messaggio in modo da vedere come interagire con questo elemento in fase di test.

Eccoci ora arrivati all’implementazione vera e propria dei test. Come anticipato in questo caso utilizzeremo SeleniumHQ che mette a disposizioni diversi strumenti per raggiungere il nostro scopo tra cui i principali sono:

Selenium IDE: Un ambiente integrato distribuito come plug-in per FireFox per la creazione rapida di test e suite di test. I test creati possono essere salvati in diversi formati ed editati direttamente dall’IDE ed essere rieseguiti in qualsiasi momento.

Selenium Remote Control (RC): Un insieme di librerie e tools per scrivere e pilotare i test direttamente da codice. Fornisce un insieme di librerie utilizzabili da svariati linguaggi di programmazione (C#, Java, PHP, Ruby e altri) e permette di eseguire i test praticamente su tutti i browser più diffusi.

Selenium Grid: Un sistema per “espandere” Selenium RC e consentire l’esecuzione dei test in parallelo su server multipli.

Partiamo con Selenium IDE in modo da poter subito prendere contatto con le possibilità offerte dal framework. Dopo averlo scaricato ed installato lo troveremo in FireFox nel menu “Tools –> Selenium IDE” e da qui possiamo lanciarlo. SeleniumIDE

Una volta aperto, l’IDE si presenterà più o meno come in figura e già in modalità di registrazione attiva (identificabile dal bottone con “pallino rosso” premuto nella parte destra della toolbar in alto), il che significa che ogni azione che noi faremo nel browser da cui lo abbiamo aperto, verrà registrata nello script corrente e potrà essere riprodotta in modo automatico rieseguendo il test in seguito. Non ci resta che inserire nella barra degli indirizzi di FireFox l’URL a cui raggiungere l’applicazione (nel nostro caso http://localhost:53771/ visto che stiamo effettuando il test di una applicazione in esecuzione in Visuall lStudio e che gira quindi su una porta creata dinamicamente del Web Development Server), premere invio per avviare la navigazione ed eseguire tutte le azioni che devono essere registrate per essere poi ripetute in seguito. Per esempio i seguenti passi:

  1. Scrivere qualcosa nella casella di testo (nell’esempio ho inserito “Bernabei”)
  2. Premere sul bottone con etichetta “Saluta via javascript”
  3. Premere OK sull’alert che appare
  4. Verificare che sull’etichetta appaia il testo “Ciao Bernabei!”

Quest’ultimo punto ovviamente non corrisponde ad un’interazione che facciamo con il browser, quindi dobbiamo “spiegare” a Selenium come farlo e questo si traduce banalmente nel premere il tasto destro del mouse sull’etichetta che vogliamo controllare. Nel menu contestuale di FireFox appariranno anche tutte le azioni che Selenium può “eseguire” su quel determinato elemento, fra le quali quella che ci verifyTextPresentinteressa che è “verifyTextPresent”. Selezionando il comando viene aggiunto nello script correntemente in registrazione l’istruzione richiesta che effettua il controllo specificato al passo numero 4.

Ora lo script è pronto per essere salvato per rieseguirlo in futuro (per farlo basta caricarlo nell’IDE e premere l’apposito pulsante “Play” della toolbar) o editato manualmente all’interno dell’ambiente. In quest’ultimo caso è possibile farlo aggiungendo i comandi alla lista visualizzata, specificando appunto il comando da eseguire selezionabile da una lista molto ampia di azioni possibili (il set di comandi disponibili è chiamato “selenese”), il target ossia l’elemento della pagina a cui il comando si riferisce ed il valore se necessario per il comando specifico. Alternativamente si può modificare lo script agendo direttamente sul “sorgente” accessibile selezionando il tab “Source” dell’IDE e modificando manualmente i comandi che verranno eseguiti dallo script (questa modalità ovviamente richiede la conoscenza dei comandi messi a disposizione dal framework in quanto non è presente un qualcosa tipo IntelliSense che può aiutare a compilare lo script.

Prima di concludere vale la pena dare uno sguardo al sorgente dello script che per default viene rappresentato usando una sintassi HTML. In pratica si tratta di un HTML contenente un tag TABLE in cui ogni riga TR corrisponde ad un comando da eseguire e contiene tre celle TD che rappresentano comando, target e valore.

Lo script appena creato viene rappresentato in HTML nel modo seguente:

   1: <?xml version="1.0" encoding="UTF-8"?>
   2: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
   3:  
   4: <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
   5: <head profile="http://selenium-ide.openqa.org/profiles/test-case">
   6:     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
   7:     <link rel="selenium.base" href="http://localhost:53771/" />
   8:     <title>New Test</title>
   9: </head>
  10:  
  11: <body>
  12: <table cellpadding="1" cellspacing="1" border="1">
  13:     <thead>
  14:         <tr><td rowspan="1" colspan="3">New Test</td></tr>
  15:     </thead>
  16:     <tbody>
  17:         <tr>
  18:             <td>open</td>
  19:             <td>/</td>
  20:             <td></td>
  21:         </tr>
  22:         <tr>
  23:             <td>type</td>
  24:             <td>txtNome</td>
  25:             <td>Bernabei</td>
  26:         </tr>
  27:         <tr>
  28:             <td>click</td>
  29:             <td>//input[@value='Saluta via javascript']</td>
  30:             <td></td>
  31:         </tr>
  32:         <tr>
  33:             <td>assertAlert</td>
  34:             <td>Stai per essere salutato...</td>
  35:             <td></td>
  36:         </tr>
  37:         <tr>
  38:             <td>verifyTextPresent</td>
  39:             <td>Ciao Bernabei!</td>
  40:             <td></td>
  41:         </tr>
  42:     </tbody>
  43: </table>
  44: </body>
  45: </html>

Dallo stesso IDE è possibile visualizzare lo script in un altro formato riutilizzabile poi da Selenium RC per eseguire il test direttamente da codice. Ad esempio scegliendo il formato C# la rappresentazione dello stesso script è la seguente:

   1: using System;
   2: using System.Text;
   3: using System.Text.RegularExpressions;
   4: using System.Threading;
   5: using NUnit.Framework;
   6: using Selenium;
   7:  
   8: namespace SeleniumTests
   9: {
  10:     [TestFixture]
  11:     public class Untitled
  12:     {
  13:         private ISelenium selenium;
  14:         private StringBuilder verificationErrors;
  15:         
  16:         [SetUp]
  17:         public void SetupTest()
  18:         {
  19:             selenium = new DefaultSelenium("localhost", 4444, "*chrome", "http://localhost:53771/");
  20:             selenium.Start();
  21:             verificationErrors = new StringBuilder();
  22:         }
  23:         
  24:         [TearDown]
  25:         public void TeardownTest()
  26:         {
  27:             try
  28:             {
  29:                 selenium.Stop();
  30:             }
  31:             catch (Exception)
  32:             {
  33:                 // Ignore errors if unable to close the browser
  34:             }
  35:             Assert.AreEqual("", verificationErrors.ToString());
  36:         }
  37:         
  38:         [Test]
  39:         public void TheUntitledTest()
  40:         {
  41:             selenium.Open("/");
  42:             selenium.Type("txtNome", "Bernabei");
  43:             selenium.Click("//input[@value='Saluta via javascript']");
  44:             Assert.AreEqual("Stai per essere salutato...", selenium.GetAlert());
  45:             try
  46:             {
  47:                 Assert.IsTrue(selenium.IsTextPresent("Ciao Bernabei!"));
  48:             }
  49:             catch (AssertionException e)
  50:             {
  51:                 verificationErrors.Append(e.Message);
  52:             }
  53:         }
  54:     }
  55: }

Da notare che se l’IDE viene impostato per rappresentare lo script nei formati per Selenium RC non sarà più possibile utilizzare la visualizzazione di default dell’IDE stesso, quella in formato tabellare che permette di aggiungere comandi selezionandoli da una ComboBox.

L’utilizzo dell’IDE consente dunque di creare in modo rapido delle suite di test (è possibile caricare più script alla volta nell’IDE ed eseguirli tutti in una volta) da rieseguire all’occorrenza per verificare il comportamento della UI. Se questo non fosse sufficiente, o se si volesse organizzare un test per controllare il funzionamento dell’applicazione su più di un browser anche diverso da FireFox, basterà utilizzare le API messe a disposizione dal Remote Control per personalizzare in modo estremamente capillare il comportamento del test.

In allegato a questo articolo è possibile scaricare i sorgenti del progetto WEB di test utilizzato e due script che “utilizzano” in modo diverso l’applicazione testandone una volta il comportamento implementato con il postback asincrono e l’altra come nell’esempio descritto in precedenza che fa uso di javascript.

Buon test a tutti Winking!

 

File allegati:

 


Currently rated 4.5 by 2 people

  • Currently 4.5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5