Zoeken…


Opmerkingen

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:

project opzet

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

  1. Maak een nieuwe klasse binnen de test .
    Klik met de rechtermuisknop op de testbronset in de projectweergave en kies New > Java class .
    Het meest gebruikte naampatroon is om de naam te gebruiken van de klasse die u gaat testen waaraan Test toegevoegd. Dus StringUtilities wordt StringUtilitiesTest .

  2. 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 de BlockJUnit4ClassRunner maar in plaats van deze direct te gebruiken, is het handiger om de alias JUnit4 wat een afkorting is voor de standaard JUnit-runner.

     @RunWith(JUnit4.class)
     public class StringUtilitiesTest {
     
     }
    
  3. 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

  1. Methode
    Om een enkele testmethode uit te voeren, klikt u met de rechtermuisknop op de methode en klikt u op Run 'addition_isCorrect()' of gebruikt u de sneltoets ctrl+shift+f10 . Voer een enkele eenheids-test uit

    Als alles correct is ingesteld, begint JUnit de methode uit te voeren en zou u de volgende interface in Android Studio moeten zien:

    Testinterface voor Android Studio-eenheden

  1. 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 op Run 'StringUtilitiesTest ' klikken of de sneltoets ctrl+shift+f10 als u de klasse in de projectweergave hebt geselecteerd.

  2. 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 op Run ... 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);
}


Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow