Android
Unit testen in Android met JUnit
Zoeken…
Opmerkingen
- Vogella: testen van eenheden met JUnit
- Junit-annotaties: java2novice.com
- Beweer Klasse: junit.org
- JUnit Api: tutorialspoint.com
- Anroid test Medium.com-berichten
Testen van lokale eenheden
Plaats hier uw /src/test/<pkg_name>/
: /src/test/<pkg_name>/
Voorbeeld testklasse
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
}
}
Afbreken
public class ExampleUnitTest {
...
}
Met de testklasse kunt u verschillende testklassen maken en deze in het testpakket plaatsen.
@Test
public void addition_isCorrect() {
...
}
Met de testmethode kunnen verschillende testmethoden worden gemaakt binnen een testklasse.
Let op de annotatie @Test
.
De testannotatie vertelt JUnit dat de openbare ongeldige methode waaraan deze is gekoppeld, als testcase kan worden uitgevoerd.
Er zijn verschillende andere handige annotaties zoals @Before
, @After
etc. Deze pagina is een goede plek om te beginnen.
assertEquals(9, c); // This test passes
assertEquals(10, c); //Test fails
Deze methoden zijn lid van de Assert
klasse. Enkele andere nuttige methoden zijn assertFalse()
, assertNotNull()
, assertTrue
enz. Hier is een uitgebreide uitleg .
Annotatie-informatie voor JUnit-test:
@Test: de testannotatie vertelt JUnit dat de openbare ongeldige methode waaraan deze is gekoppeld, als testcase kan worden uitgevoerd. Om de methode uit te voeren, construeert JUnit eerst een nieuwe instantie van de klasse en roept vervolgens de geannoteerde methode op.
@Vooraf: bij het schrijven van tests is het gebruikelijk om te ontdekken dat voor verschillende tests vergelijkbare objecten moeten worden gemaakt voordat ze kunnen worden uitgevoerd. @Before
een openbare ongeldige methode met @Before
zorgt ervoor dat die methode wordt uitgevoerd vóór de testmethode.
@Na: als u externe bronnen toewijst in de methode Before, moet u deze vrijgeven nadat de test is uitgevoerd. @After
een openbare ongeldige methode met @After
zorgt ervoor dat die methode wordt uitgevoerd na de testmethode. Alle @After
methoden worden gegarandeerd uitgevoerd, zelfs als een methode Before of Test een uitzondering @After
Tip Maak snel testklassen in Android Studio
- Plaats de cursor op de klassennaam waarvoor u een testklasse wilt maken.
- Druk op Alt + Enter (Windows).
- Selecteer Test maken en druk op Return.
- Selecteer de methoden waarvoor u testmethoden wilt maken en klik op OK.
- Selecteer de map waarin u de testklasse wilt maken.
- Je bent klaar, dit is je eerste test.
Tip Voer eenvoudig tests uit in Android Studio
- Klik met de rechtermuisknop op het pakket testen.
- Selecteer Run 'Tests in ...
- Alle tests in het pakket worden in één keer uitgevoerd.
Bedrijfslogica verplaatsen van Android-componenten
Veel van de waarde van lokale JVM-eenheidstests komt van de manier waarop u uw toepassing ontwerpt. U moet het zodanig ontwerpen dat u uw bedrijfslogica kunt loskoppelen van uw Android-componenten. Hier is een voorbeeld van een dergelijke manier met behulp van het Model-View-Presenter-patroon . Laten we dit oefenen door een eenvoudig aanmeldscherm te implementeren dat alleen een gebruikersnaam en wachtwoord gebruikt. Onze Android-app is verantwoordelijk voor het valideren dat de gebruikersnaam die de gebruiker opgeeft niet leeg is en dat het wachtwoord ten minste acht tekens lang is en ten minste één cijfer bevat. Als de gebruikersnaam / het wachtwoord geldig is, voeren we onze aanmeld-API-oproep uit, anders geven we een foutmelding weer.
Voorbeeld waarbij bedrijfslogica sterk gekoppeld is aan Android Component.
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();
}
}
}
Voorbeeld waarbij bedrijfslogica is losgekoppeld van Android-component.
Hier definiëren we in een enkele klasse, LoginContract, die de verschillende interacties tussen onze verschillende klassen huisvest.
public interface LoginContract {
public interface View {
performSignUpApiCall(String username, String password);
displayInvalidCredentialsErrorMessage();
}
public interface Presenter {
void validateUserCredentials(String username, String password);
}
}
Onze LoginActivity is grotendeels hetzelfde, behalve dat we de verantwoordelijkheid hebben verwijderd om te moeten weten hoe we het aanmeldingsformulier van een gebruiker moeten valideren (onze bedrijfslogica). De LoginActivity zal nu vertrouwen op ons nieuwe LoginPresenter om validatie uit te voeren.
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);
}
...
}
Nu bevindt uw bedrijfslogica zich in uw nieuwe klasse 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();
}
}
}
En nu kunnen we lokale JVM-eenheidstests maken op basis van uw nieuwe klasse 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);
}
}
Zoals u ziet, toen we onze bedrijfslogica uit de LoginActivity haalden en in de LoginPresenter POJO plaatsten. We kunnen nu lokale JVM-eenheidstests maken op basis van onze bedrijfslogica.
Opgemerkt moet worden dat er verschillende andere implicaties zijn van onze verandering in de architectuur, zoals het feit dat we bijna vasthouden aan elke klasse met een enkele verantwoordelijkheid, extra klassen, enz. Dit zijn slechts neveneffecten van de manier waarop ik ervoor kies dit uit te voeren ontkoppeling via de MVP-stijl. MVP is slechts een manier om dit aan te pakken, maar er zijn andere alternatieven waarnaar u misschien wilt kijken, zoals MVVM . Je hoeft alleen het beste systeem te kiezen dat voor jou werkt.
Aan de slag met JUnit
Opstelling
Om te beginnen met het testen van je Android-project met JUnit, moet je de JUnit-afhankelijkheid aan je project toevoegen en moet je een testbron-set maken die de broncode voor de unit-tests gaat bevatten. Projecten gemaakt met Android Studio bevatten vaak al de JUnit-afhankelijkheid en de testbron-set
Voeg de volgende regel toe aan uw module build.gradle
bestand binnen de afhankelijkheden Closure
:
testCompile 'junit:junit:4.12'
JUnit-testklassen bevinden zich in een speciale bronset met de naam test
. Als deze bronset niet bestaat, moet u zelf een nieuwe map maken. De mappenstructuur van een standaard Android Studio-project (gebaseerd op Gradle) ziet er als volgt uit:
<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)
Als uw project niet de map /app/src/test
heeft, moet u het zelf maken. In de test
hebt u ook een java
map nodig (maak deze aan als deze niet bestaat). De Java-map in de test
moet dezelfde pakketstructuur bevatten als uw main
.
Als de configuratie correct is, moet uw projectstructuur (in de Android-weergave in Android Studio) er als volgt uitzien:
Opmerking: u hoeft niet per se de androidTest
te hebben, deze androidTest
wordt vaak gevonden in projecten die zijn gemaakt door Android Studio en is hier ter referentie opgenomen.
Een test schrijven
Maak een nieuwe klasse binnen de
test
.
Klik met de rechtermuisknop op de testbronset in de projectweergave en kiesNew
>Java class
.
Het meest gebruikte naampatroon is om de naam te gebruiken van de klasse die u gaat testen waaraanTest
toegevoegd. DusStringUtilities
wordtStringUtilitiesTest
.Voeg de annotatie
@RunWith
De annotatie@RunWith
is nodig om JUnit de tests te laten uitvoeren die we in onze@RunWith
gaan definiëren. De standaard JUnit-runner (voor JUnit 4) is deBlockJUnit4ClassRunner
maar in plaats van deze direct te gebruiken, is het handiger om de aliasJUnit4
wat een afkorting is voor de standaard JUnit-runner.@RunWith(JUnit4.class) public class StringUtilitiesTest { }
Maak een test
Een eenheidstest is in wezen slechts een methode die in de meeste gevallen niet mag mislukken als deze wordt uitgevoerd. Met andere woorden, het zou geen uitzondering mogen vormen. Binnen een testmethode vindt u bijna altijd beweringen die controleren of aan specifieke voorwaarden is voldaan. Als een bewering faalt, wordt een uitzondering gegenereerd waardoor de methode / test mislukt. Een testmethode is altijd voorzien van de aantekening@Test
. Zonder deze annotatie zal JUnit de test niet automatisch uitvoeren.@RunWith(JUnit4.class) public class StringUtilitiesTest { @Test public void addition_isCorrect() throws Exception { assertEquals("Hello JUnit", "Hello" + " " + "JUnit"); } }
Opmerking: in tegenstelling tot de standaard Java-methode naamgeving conventie-eenheid testmethode namen bevatten vaak onderstrepingstekens.
Een test uitvoeren
Methode
Om een enkele testmethode uit te voeren, klikt u met de rechtermuisknop op de methode en klikt u opRun 'addition_isCorrect()'
of gebruikt u de sneltoetsctrl+shift+f10
.Als alles correct is ingesteld, begint JUnit de methode uit te voeren en zou u de volgende interface in Android Studio moeten zien:
Klasse
U kunt ook alle tests uitvoeren die in een enkele klasse zijn gedefinieerd, door met de rechtermuisknop op de klasse in de projectweergave te klikken en opRun 'StringUtilitiesTest '
klikken of de sneltoetsctrl+shift+f10
als u de klasse in de projectweergave hebt geselecteerd.Pakket (alles)
Als u niet alle tests wilt uitvoeren die in het project of in een pakket zijn gedefinieerd, kunt u met de rechtermuisknop op het pakket klikken en opRun ...
net zoals u alle tests uitvoert die in een enkele klasse zijn gedefinieerd.
Uitzonderingen
JUnit kan ook worden gebruikt om te testen of een methode een specifieke uitzondering genereert voor een bepaalde invoer.
In dit voorbeeld testen we of de volgende methode echt een uitzondering genereert als het Booleaanse formaat (invoer) niet wordt herkend / onbekend:
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);
}
}
Door de expected
parameter toe te voegen aan de @Test
annotatie, kan worden bepaald welke uitzondering naar verwachting wordt gegenereerd. De eenheidstest zal mislukken als deze uitzondering niet optreedt en slagen als de uitzondering inderdaad wordt gegenereerd:
@Test(expected = IllegalArgumentException.class)
public void parseBoolean_parsesInvalidFormat_throwsException(){
StringUtilities.parseBoolean("Hello JUnit");
}
Dit werkt goed, maar het beperkt je tot slechts één testcase binnen de methode. Soms wilt u misschien meerdere cases binnen een enkele methode testen. Een techniek die vaak wordt gebruikt om deze beperking te overwinnen, is het gebruik van try-catch
blokken en de methode 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){
}
}
Opmerking: sommige mensen beschouwen het als een slechte gewoonte om meer dan één geval binnen een eenheidstest te testen.
Statische invoer
JUnit definieert nogal wat assertEquals
methoden, minimaal één voor elk primitief type en één voor objecten is beschikbaar. Deze methoden zijn standaard niet direct beschikbaar om aan te roepen en moeten als volgt worden aangeroepen: Assert.assertEquals
. Maar omdat deze methoden zo vaak worden gebruikt, gebruiken mensen bijna altijd een statische import zodat de methode direct kan worden gebruikt alsof deze deel uitmaakt van de klasse zelf.
Gebruik de volgende importinstructie om een statische import voor de methode assertEquals
toe te voegen:
import static org.junit.Assert.assertEquals;
U kunt ook alle assert-methoden statisch importeren, inclusief assertArrayEquals
, assertNotNull
en assertFalse
etc. met behulp van de volgende statische import:
import static org.junit.Assert.*;
Zonder statische import:
@Test
public void addition_isCorrect(){
Assert.assertEquals(4 , 2 + 2);
}
Met statische import:
@Test
public void addition_isCorrect(){
assertEquals(4 , 2 + 2);
}