Android
Test delle unità in Android con JUnit
Ricerca…
Osservazioni
- Vogella: unit test con JUnit
- Annotazioni Junit : java2novice.com
- Assert Class: junit.org
- JUnit Api: tutorialspoint.com
- Anroid che prova i messaggi su Medium.com
Creazione di test di unità locali
Inserisci qui le tue classi di test: /src/test/<pkg_name>/
Esempio di test di classe
public class ExampleUnitTest {
@Test
public void addition_isCorrect() throws Exception {
int a=4, b=5, c;
c = a + b;
assertEquals(9, c); // This test passes
assertEquals(10, c); //Test fails
}
}
Abbattersi
public class ExampleUnitTest {
...
}
La classe di test, è possibile creare diverse classi di test e inserirle all'interno del pacchetto di test.
@Test
public void addition_isCorrect() {
...
}
Il metodo di prova, diversi metodi di prova possono essere creati all'interno di una classe di test.
Notare l'annotazione @Test
.
L'annotazione di Test dice a JUnit che il metodo pubblico vuoto a cui è collegato può essere eseguito come un caso di test.
Ci sono molte altre annotazioni utili come @Before
, @After
ecc. Questa pagina sarebbe un buon punto di partenza.
assertEquals(9, c); // This test passes
assertEquals(10, c); //Test fails
Questi metodi sono membri della classe Assert
. Alcuni altri metodi utili sono assertFalse()
, assertNotNull()
, assertTrue
ecc. Ecco una spiegazione dettagliata .
Informazioni sull'annotazione per il test JUnit:
@ Test : l'annotazione Test dice a JUnit che il metodo di annullamento pubblico a cui è collegato può essere eseguito come un caso di test. Per eseguire il metodo, JUnit prima costruisce una nuova istanza della classe, quindi richiama il metodo annotato.
@Prima: quando si scrivono i test, è comune scoprire che per diversi test sono necessari oggetti simili creati prima che possano essere eseguiti. @Before
un metodo public void con @Before
, il metodo viene eseguito prima del metodo Test.
@After: se si assegnano risorse esterne in un metodo Before è necessario rilasciarle dopo l'esecuzione del test. @After
un metodo public void con @After
, il metodo viene eseguito dopo il metodo Test. Tutti i metodi @After
sono garantiti per l'esecuzione anche se un metodo Before o Test genera un'eccezione
Suggerimento Crea rapidamente classi di test in Android Studio
- Posiziona il cursore sul nome della classe per il quale vuoi creare una classe di test.
- Premi Alt + Invio (Windows).
- Seleziona Crea test, premi Invio.
- Selezionare i metodi per cui si desidera creare metodi di prova, fare clic su OK.
- Seleziona la directory in cui desideri creare la classe di test.
- Il gioco è fatto, questo è il tuo primo test.
Suggerimento Esegui facilmente i test in Android Studio
- Fare clic con il tasto destro sul pacchetto.
- Seleziona Esegui "Test in ...
- Tutti i test nel pacchetto verranno eseguiti contemporaneamente.
Spostamento della logica aziendale dai componenti Android
Gran parte del valore dei test delle unità JVM locali deriva dal modo in cui si progetta la propria applicazione. Devi progettarlo in modo tale da poter disgiungere la tua logica di business dai tuoi componenti Android. Ecco un esempio di questo modo utilizzando il modello Model-View-Presenter . Tienilo in pratica implementando una schermata di registrazione di base che richiede solo un nome utente e una password. La nostra app per Android è responsabile della convalida che il nome utente fornito dall'utente non sia vuoto e che la password sia lunga almeno otto caratteri e contenga almeno una cifra. Se il nome utente / password è valido, eseguiamo la nostra chiamata API di iscrizione, altrimenti viene visualizzato un messaggio di errore.
Esempio in cui la logica aziendale è fortemente accoppiata con Componente Android.
public class LoginActivity extends Activity{
...
private void onSubmitButtonClicked(){
String username = findViewById(R.id.username).getText().toString();
String password = findViewById(R.id.password).getText().toString();
boolean isUsernameValid = username != null && username.trim().length() != 0;
boolean isPasswordValid = password != null && password.trim().length() >= 8 && password.matches(".*\\d+.*");
if(isUsernameValid && isPasswordValid){
performSignUpApiCall(username, password);
} else {
displayInvalidCredentialsErrorMessage();
}
}
}
Esempio in cui la logica aziendale è disaccoppiata dal componente Android.
Qui definiamo in una singola classe, LoginContract, che ospiterà le varie interazioni tra le nostre varie classi.
public interface LoginContract {
public interface View {
performSignUpApiCall(String username, String password);
displayInvalidCredentialsErrorMessage();
}
public interface Presenter {
void validateUserCredentials(String username, String password);
}
}
Il nostro LoginActivity è per lo più lo stesso eccetto che abbiamo rimosso la responsabilità di dover sapere come convalidare il modulo di iscrizione di un utente (la nostra logica di business). LoginActivity ora farà affidamento sul nostro nuovo LoginPresenter per eseguire la convalida.
public class LoginActivity extends Activity implements LoginContract.View{
private LoginContract.Presenter presenter;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
presenter = new LoginPresenter(this);
....
}
...
private void onSubmitButtonClicked(){
String username = findViewById(R.id.username).getText().toString();
String password = findViewById(R.id.password).getText().toString();
presenter.validateUserCredentials(username, password);
}
...
}
Ora la tua logica aziendale risiederà nella tua nuova classe LoginPresenter.
public class LoginPresenter implements LoginContract.Presenter{
private LoginContract.View view;
public LoginPresenter(LoginContract.View view){
this.view = view;
}
public void validateUserCredentials(String username, String password){
boolean isUsernameValid = username != null && username.trim().length() != 0;
boolean isPasswordValid = password != null && password.trim().length() >= 8 && password.matches(".*\\d+.*");
if(isUsernameValid && isPasswordValid){
view.performSignUpApiCall(username, password);
} else {
view.displayInvalidCredentialsErrorMessage();
}
}
}
E ora possiamo creare test di unità JVM locali contro la tua nuova classe LoginPresenter.
public class LoginPresenterTest {
@Mock
LoginContract.View view;
private LoginPresenter presenter;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
presenter = new LoginPresenter(view);
}
@Test
public void test_validateUserCredentials_userDidNotEnterUsername_displayErrorMessage() throws Exception {
String username = "";
String password = "kingslayer1";
presenter.validateUserCredentials(username, password);
Mockito.verify(view). displayInvalidCredentialsErrorMessage();
}
@Test
public void test_validateUserCredentials_userEnteredFourLettersAndOneDigitPassword_displayErrorMessage() throws Exception {
String username = "Jaime Lanninster";
String password = "king1";
presenter.validateUserCredentials(username, password);
Mockito.verify(view). displayInvalidCredentialsErrorMessage();
}
@Test
public void test_validateUserCredentials_userEnteredNineLettersButNoDigitsPassword_displayErrorMessage() throws Exception {
String username = "Jaime Lanninster";
String password = "kingslayer";
presenter.validateUserCredentials(username, password);
Mockito.verify(view). displayInvalidCredentialsErrorMessage();
}
@Test
public void test_validateUserCredentials_userEnteredNineLettersButOneDigitPassword_performApiCallToSignUpUser() throws Exception {
String username = "Jaime Lanninster";
String password = "kingslayer1";
presenter.validateUserCredentials(username, password);
Mockito.verify(view).performSignUpApiCall(username, password);
}
}
Come puoi vedere, quando abbiamo estratto la nostra logica aziendale da LoginActivity e l'abbiamo inserita nel POJO LoginPresenter. Ora possiamo creare test di unità JVM locali contro la nostra logica aziendale.
Dovrebbe essere notato che ci sono varie altre implicazioni dal nostro cambiamento di architettura, come il fatto che siamo vicini all'adesione ad ogni classe che ha una singola responsabilità, classi aggiuntive, ecc. Questi sono solo effetti collaterali del modo in cui scelgo di fare questo disaccoppiamento tramite lo stile MVP. MVP è solo un modo per farlo, ma ci sono altre alternative che potresti voler considerare come MVVM . Devi solo scegliere il miglior sistema che funzioni per te.
Iniziare con JUnit
Impostare
Per avviare l'unità testando il tuo progetto Android usando JUnit è necessario aggiungere la dipendenza JUnit al progetto ed è necessario creare un set di sorgenti di test che contenga il codice sorgente per i test di unità. I progetti creati con Android Studio spesso includono già la dipendenza JUnit e il set di origini di test
Aggiungi la seguente riga al tuo file build.gradle
modulo all'interno delle dipendenze Closure
:
testCompile 'junit:junit:4.12'
Le classi di test JUnit si trovano in un test
denominato set di origine. Se questo set di sorgenti non esiste devi creare tu stesso una nuova cartella. La struttura delle cartelle di un progetto predefinito di Android Studio (basato sul gradiente) ha il seguente aspetto:
<project-root-folder>
/app (module root folder)
/build
/libs
/src
/main (source code)
/test (unit test source code)
/androidTest (instrumentation test source code)
build.gradle (module gradle file)
/build
/gradle
build.gradle (project gradle file)
gradle.properties
gradlew
gradlew.bat
local.properties
settings.gradle (gradle settings)
Se il tuo progetto non ha la cartella /app/src/test
devi crearlo da solo. All'interno della cartella di test
è anche necessaria una cartella java
(creala se non esiste). La cartella java nel set sorgente di test
dovrebbe contenere la stessa struttura del pacchetto del set sorgente main
.
Se la configurazione è corretta, la struttura del progetto (nella vista Android in Android Studio) dovrebbe essere simile alla seguente:
Nota: non è necessario disporre del androidTest
origini androidTest
, questo set di origine si trova spesso nei progetti creati da Android Studio ed è incluso qui per riferimento.
Scrivere un test
Crea una nuova classe all'interno del set di sorgenti di
test
.
Fare clic con il tasto destro del mouse sull'origine del test nella vista del progetto, selezionareNew
>Java class
.
Il modello di denominazione più utilizzato consiste nell'utilizzare il nome della classe che si testerà conTest
aggiunto ad esso. QuindiStringUtilities
diventaStringUtilitiesTest
.Aggiungi l'annotazione
@RunWith
L'annotazione@RunWith
è necessaria per fare in modo che JUnit esegua i test che definiremo nella nostra classe di test. Il JUnit runner predefinito (per JUnit 4) è ilBlockJUnit4ClassRunner
ma invece di usare direttamente questo è più comodo usare l'aliasJUnit4
che è una scorciatoia per il runner JUnit predefinito.@RunWith(JUnit4.class) public class StringUtilitiesTest { }
Crea un test
Un test unitario è essenzialmente solo un metodo che, nella maggior parte dei casi, non dovrebbe fallire se eseguito. In altre parole, non dovrebbe fare eccezione. All'interno di un metodo di test troverai quasi sempre asserzioni che verificano se sono soddisfatte condizioni specifiche. Se un'asserzione fallisce, genera un'eccezione che causa il fallimento del metodo / test. Un metodo di prova è sempre annotato con l'annotazione@Test
. Senza questa annotazione, JUnit non eseguirà automaticamente il test.@RunWith(JUnit4.class) public class StringUtilitiesTest { @Test public void addition_isCorrect() throws Exception { assertEquals("Hello JUnit", "Hello" + " " + "JUnit"); } }
Nota: a differenza del metodo di denominazione dei metodi standard, i nomi dei metodi di test dell'unità di misura spesso contengono caratteri di sottolineatura.
Esecuzione di un test
Metodo
Per eseguire un singolo metodo di prova, è possibile fare clic con il tasto destro sul metodo e fare clic suRun 'addition_isCorrect()'
o utilizzare la scorciatoia da tastieractrl+shift+f10
.Se tutto è impostato correttamente, JUnit avvia il metodo e dovresti vedere la seguente interfaccia in Android Studio:
Classe
È inoltre possibile eseguire tutti i test definiti in una singola classe, facendo clic con il tasto destro sulla classe nella vista del progetto e facendo clic suRun 'StringUtilitiesTest '
o utilizzare la scorciatoia da tastieractrl+shift+f10
se si è selezionata la classe nella vista di progetto.Pacchetto (tutto)
Se non si desidera eseguire tutti i test definiti nel progetto o in un pacchetto, è sufficiente fare clic con il pulsante destro del mouse sul pacchetto e fare clic suRun ...
proprio come si eseguiranno tutti i test definiti in una singola classe.
eccezioni
JUnit può anche essere usato per verificare se un metodo genera un'eccezione specifica per un dato input.
In questo esempio testeremo se il seguente metodo genera un'eccezione se il formato booleano (input) non è riconosciuto / sconosciuto:
public static boolean parseBoolean(@NonNull String raw) throws IllegalArgumentException{
raw = raw.toLowerCase().trim();
switch (raw) {
case "t": case "yes": case "1": case "true":
return true;
case "f": case "no": case "0": case "false":
return false;
default:
throw new IllegalArgumentException("Unknown boolean format: " + raw);
}
}
Aggiungendo il parametro expected
all'annotazione @Test
, è possibile definire quale eccezione dovrebbe essere generata. Il test unitario fallirà se questa eccezione non si verifica e riuscirà se l'eccezione è effettivamente lanciata:
@Test(expected = IllegalArgumentException.class)
public void parseBoolean_parsesInvalidFormat_throwsException(){
StringUtilities.parseBoolean("Hello JUnit");
}
Funziona bene, tuttavia, ti limita a un solo caso di test all'interno del metodo. A volte potresti voler testare più casi con un singolo metodo. Una tecnica spesso utilizzata per superare questa limitazione è l'utilizzo dei blocchi try-catch
e del metodo Assert.fail()
:
@Test
public void parseBoolean_parsesInvalidFormats_throwsException(){
try {
StringUtilities.parseBoolean("Hello!");
fail("Expected IllegalArgumentException");
} catch(IllegalArgumentException e){
}
try {
StringUtilities.parseBoolean("JUnit!");
fail("Expected IllegalArgumentException");
} catch(IllegalArgumentException e){
}
}
Nota: alcune persone ritengono che sia una cattiva pratica testare più di un singolo caso all'interno di un test unitario.
Importazione statica
JUnit definisce alcuni metodi assertEquals
almeno uno per ogni tipo primitivo e uno per gli oggetti è disponibile. Questi metodi sono di default non direttamente disponibili per chiamare e dovrebbero essere chiamati così: Assert.assertEquals
. Ma poiché questi metodi vengono utilizzati così spesso, le persone utilizzano quasi sempre un'importazione statica, in modo che il metodo possa essere utilizzato direttamente come se fosse parte della classe stessa.
Per aggiungere un'importazione statica per il metodo assertEquals
, utilizzare la seguente dichiarazione di importazione:
import static org.junit.Assert.assertEquals;
È inoltre possibile importare statici tutti i metodi di assert, inclusi assertArrayEquals
, assertNotNull
e assertFalse
ecc. Utilizzando la seguente importazione statica:
import static org.junit.Assert.*;
Senza importazione statica:
@Test
public void addition_isCorrect(){
Assert.assertEquals(4 , 2 + 2);
}
Con l'importazione statica:
@Test
public void addition_isCorrect(){
assertEquals(4 , 2 + 2);
}