5 Novembre, 2018 | Di

Test Automatico: testare una App su dispositivi reali in Cloud

Test Automatico

Il testing è un’attività onerosa in termini di tempo e concentrazione richiesti. Ad ogni nuova modifica, diventa necessario eseguire di nuovo i test per sincerarsi non solo che le nuove caratteristiche funzionino, ma anche che non vi siano state regressioni nell’intera App. Si tratta di un’operazione complessa, che può richiedere molto tempo, e che è sempre esposta all’errore umano. Il Test Automatico, sebbene richieda un investimento iniziale, può essere una soluzione fondamentale per risparmiare tempo ed energie.

In questo articolo, illustreremo una possibile Stack per l’automazione di test mobile che abbiamo realmente utilizzato per testare l’App di un cliente.

La nostra Stack

Ciò che volevamo fare era scrivere degli scenari di test, automatizzarne lo svolgimento, ed eseguirli in Cloud, ottenendo un report dei risultati ottenuti.

Posto che il linguaggio di programmazione scelto è stato Java, abbiamo composto la nostra Stack così: Cucumber/Gherkin, Appium Studio, TestNG, BrowserStack Automate.

Cucumber/Gherkin

Volevamo che gli scenari potessero essere condivisi con il cliente, avevamo quindi bisogno di poterli scrivere in un modo che fosse comprensibile anche a qualcuno privo di particolari nozioni tecniche. Cucumber permette di scrivere degli scenari di test in linguaggio naturale, basandosi sulla Gherkin: una semplice sintassi da seguire e poche facili parole chiave da utilizzare.

Scritto in Gherkin uno scenario, nella sua forma essenziale, si presenta così:

 

Given an initial condition

When I do something

Then something happen

 

Per esempio:

 

Given I am in the login page

When I insert the right Username and Password

Then I enter in the application and arrive on Home Page

 

Si tratta di una serie di passi, scritti in linguaggio naturale, preceduti dalle parole chiave Given, When e Then (ce ne sono altre, come And, ma queste sono quelle essenziali).

Scritti questi passi e gli scenari Cucumber permette di abbinare ad ogni passo un metodo che esegue l’azione descritta, o verifica la condizione richiesta, dal passo.

L’abbinamento tra scenari e step definitions avviene tramite annotazioni. Per abbinare ad esempio un metodo al passo “When I insert the right Username and Password”, scriverò:

@ When (“I insert the right Username and Password”)
public void I_insert_the_right_Username_and_Password () {
  //Some code here
}

Dare al metodo il nome del passo non è obbligatorio, ma può essere una buona pratica.

Su Eclipse è disponibile un plugin dedicato al Behaviour Driven Development, che facilita la gestione dei file .feature (l’estensione dei file di Cucumber) e l’abbinamento tra scenari e step definitions.

Appium / Appium Studio

Appium è un framework open source per l’automazione di test su app native, ibride o web. Appium Studio è un tool di automazione che genera automaticamente script in diversi linguaggi delle operazioni svolte.

Con Appium studio è possibile collegare un device Android o IOS (o simularlo), importare una applicazione (anche da device), registrare delle operazioni compiute sull’app, e al termine ottenere un test definito step by step, e il codice già pronto (da adattare) nel linguaggio selezionato. Nel nostro caso  Java(TestNG), ma sono disponibili anche Phyton, C#, Ruby, Java(JUnit).

Collegato il device, scelta l’applicazione, Appium creerà un mirror del dispositivo, su cui sarà possibile usare l’App, registrare l’attività, e adoperare lo strumento Object Spy per ispezionare il Layout e tutti gli elementi che contiene, recuperandone gli Id e gli xpath.

Potremo poi inserire il codice ottenuto tramite Appium, opportunamente adattato, all’interno dei metodi o delle funzioni abbinate agli step. Ad es. il metodo per il login potrà essere:

@ When (“I insert the right Username and Password”)
public void I_insert_the_right_Username_and_Password () {
    AndroidElement searchElement = (AndroidElement) new WebDriverWait(driver, 10).until(
      ExpectedConditions.presenceOfElementLocated(By.id(“login_field”)));
    searchElement.sendKeys("my_username");
    AndroidElement searchElement = (AndroidElement) new WebDriverWait(driver, 10).until(
      ExpectedConditions.presenceOfElementLocated(By.id(“password_field”)));
    searchElement.sendKeys("my_password");
}

TestNG

TestNG è un framework di test alternativo al più conosciuto JUnit, che permette una buona integrazione con BrowserStack, la piattaforma di test in cloud per web e mobile che volevamo usare.

TestNG capisce se una classe o un metodo sono da testare tramite l’annotazione @Test. Nel nostro caso però non volevamo eseguire semplicemente dei TestNG test, ma usare TestNG per integrare Cucumber e BrowserStack. Questo vuol dire che il codice dei nostri test non si troverà direttamente nella classe contenente le annotazioni di TestNG – infatti, come abbiamo visto, si trova nelle step definitions abbinate agli scenari scritti in Gherkin – e che la classe di TestNG avrà invece il ruolo di runner, sarà cioè la classe scatenante l’esecuzione degli scenari in cucumber.

Ecco la classe runner che abbiamo creato:

@CucumberOptions(	
    features = "src/test/resources/feautures/<stepdefinition_directory>",        
    glue = {"stepdefs"},
    plugin = {
        "pretty",
        "html:target/cucumber-reports/cucumber-pretty",
        "json:target/cucumber-reports/CucumberTestReport.json",
        "rerun:target/cucumber-reports/rerun.txt"
    }
)
 
public class TestRunner {
    private TestNGCucumberRunner testNGCucumberRunner;
 
    @BeforeClass(alwaysRun = true)
    public void setUpClass() throws Exception {
        testNGCucumberRunner = new TestNGCucumberRunner(this.getClass());
    }
    @Test(groups = "cucumber", description = "Runs Cucumber Feature", dataProvider = "features")
    public void feature(CucumberFeatureWrapper cucumberFeature) {
    	testNGCucumberRunner.runCucumber(cucumberFeature.getCucumberFeature());
    }
    @DataProvider
    public Object[][] features() {
        return testNGCucumberRunner.provideFeatures();
    }
    @AfterClass(alwaysRun = true)
    public void tearDownClass() throws Exception {
        testNGCucumberRunner.finish();
    }
}

Come si può notare, ha la struttura di una classe di TestNG, con le annotazioni @BeforeClass, @Test e @AfterClass, ma con qualcosa di particolare in più: l’uso della classe testNGCucumberRunner, e dell’annotazione @CucumberOptions.

È attraverso quest’ultima annotazione che passiamo i settaggi del test, indicando ad esempio dove si trovano gli scenari (features = "src/test/resources/feautures"), dove si trovano le stepdefinitions (glue = {"stepdefs"}), e come vogliamo composto il report finale (plugin={…}).

Ricapitolando lanciando la nostra classe runner come un TestNG test, questa richiamerà e farà partire le features di Cucumber. A partire dagli scenari descritti nei file .feature, posti nella cartella <stepdefinition_directory>, verranno eseguiti i metodi definiti nelle stepdefinitions.

Sarà qui che si troverà il nostro codice, ricavato e testato su Appium Studio, per simulare l’uso di una applicazione Android.

BrowserStack

Fin qui abbiamo visto l’integrazione tra Cucumber/Gherkin, TestNG, e Appium, ma questo non basta: dobbiamo ancora collegarci con il servizio in cloud di BrowserStack. Operazione semplificata dal fatto che BrowserStack offre una guida all’automazione di test per app mobile e all’integrazione con TestNG.

Il primo passo è caricare l’applicazione, con queste istruzioni:

curl -u "<nomeutente>:<password>" \
-X POST "https://api-cloud.browserstack.com/app-automate/upload" \
-F "file=@/path/to/app/file/Application-debug.apk"

Una volta effettuata questa operazione ci verrà fornito un codice identificativo dell’App, che dovremo passare a BrowserStack, tra le capabilities settate per il driver, al momento del collegamento (Es. ("app", "bs://<hashed appid>")).

Le capabilities sono coppie chiave-valore che permettono la customizzazione dei test automatici su BrowserStack. La guida propone un modulo d’esempio per l’integrazione tra TestNG e Browserstack. Di cui la prima parte legge la configurazione da un file JSON e la successiva configura le capabilities distinguendo fra Common Capabilities:

Test Automatico

e Environments Capabilities:

Test Automatico

Quindi vengono passati al driver l’URL a cui collegarsi e le capabilities:

driver = new AndroidDriver(new URL("http://"+username+":"+accessKey+"@"+config.get("server")+"/wd/hub"), capabilities);

Nel nostro caso, siamo partiti da questo modulo, ma lo abbiamo adattato a Cucumber, mantenendo la classe di TestNG come classe con il mero ruolo di TestRunner, già illustrato. Inoltre abbiamo preferito passare la configurazione delle capabilities attraverso un file yaml.

Nel nostro progetto le capabilities non vengono definite nella classe TestRunner, ma nelle stepdefinitions. Mentre per usare i file yaml, abbiamo usato la libreria Snakeyaml, che permette di abbinare a un file yaml un oggetto POJO, da cui si può ritrovare ciò che occorre con i metodi get. Ecco un esempio dal nostro progetto:

this.config_file="config.yml";		  
InputStream ios = new FileInputStream("src/test/resources/conf/" + config_file );
ConfigurationBean configuration = yaml.loadAs(ios, ConfigurationBean.class );

In conclusione

Grazie a Gherkin e Cucumber possiamo scrivere i nostri test in linguaggio naturale, in modo che siano comprensibili senza particolari conoscenze tecniche, e abbinarli step by step al nostro codice; grazie ad Appium e ad Appium Studio, possiamo analizzare la nostra applicazione e avere una prima bozza del codice delle automazioni; grazie a TestNg possiamo integrare il nostro progetto con BrowserStack così da farlo girare in Cloud, sui numerosi dispositivi Android e IOS messi a disposizione. E grazie a tutto questo possiamo risparmiare tempo e fatica, lasciando che i nostri test vadano da soli. Limitandoci ad analizzare il report finale.

Note

Le librerie utilizzate nel progetto sono: