Java >> Java opplæring >  >> Java

PowerMockito-veiledning for nybegynnere

En enhetstest skal teste en klasse isolert. Bivirkninger fra andre klasser eller systemet bør elimineres hvis mulig. Mockito lar deg skrive vakre tester med en ren og enkel API. I dette eksemplet skal vi lære om PowerMockito som er en utvidelse av Mockito. PowerMockito utvider Mockito-funksjonaliteten med flere nye funksjoner som hånende statiske og private metoder og mer. Verktøy og teknologier som brukes i dette eksemplet er Java 1.8, Eclipse Luna 4.4.2

Innholdsfortegnelse

1. Innledning
2. Hånende
3. Opprette et prosjekt
3.1 Avhengigheter
4. Enkelt eksempel
4.1 domeneklasse
4.2 Brukertjeneste
4.3 Brukerkontroller
4.4 Test av brukerkontroller
5. Hånte statiske metoder
6. Hånlige private metoder
7. Hånte sluttklasser
8. Spotkonstruktør
9. Last ned kildefilen

1. Introduksjon

Å skrive enhetstester kan være vanskelig, og noen ganger må god design ofres for testbarhetens formål. Ofte tilsvarer testbarhet god design, men dette er ikke alltid tilfelle. For eksempel kan endelige klasser og metoder ikke brukes, private metoder må noen ganger beskyttes eller unødvendig flyttes til en samarbeidspartner, statiske metoder bør unngås helt og så videre ganske enkelt på grunn av begrensningene til eksisterende rammeverk.

Mockito er et populært hånlig rammeverk som kan brukes sammen med JUnit. Mockito lar oss lage og konfigurere falske objekter. Å bruke Mockito forenkler utviklingen av tester for klasser med eksterne avhengigheter betydelig. Vi kan lage de hånlige objektene manuelt eller kan bruke de spottende rammene som Mockito, EasyMock. jMock osv. Mock-rammeverk lar oss lage falske objekter under kjøring og definere oppførselen deres. Det klassiske eksemplet for et falskt objekt er en dataleverandør. I produksjonen brukes en ekte database, men for å teste simulerer et falskt objekt databasen og sikrer at testforholdene alltid er de samme.

PowerMock er et rammeverk som utvider andre mock-biblioteker som EasyMock med kraftigere funksjoner. PowerMock bruker en tilpasset klasselaster og bytekodemanipulering for å muliggjøre hån mot statiske metoder, konstruktører, endelige klasser og metoder, private metoder, fjerning av statiske initialiseringer og mer. Ved å bruke en tilpasset klasselaster trenger ingen endringer å gjøres på IDE eller kontinuerlige integrasjonsservere som forenkler adopsjon. Utviklere som er kjent med de støttede mock-rammeverkene vil finne PowerMock enkel å bruke, siden hele forventnings-APIet er det samme, både for statiske metoder og konstruktører. PowerMock har som mål å utvide de eksisterende API-ene med et lite antall metoder og merknader for å aktivere ekstrafunksjonene. For øyeblikket støtter PowerMock EasyMock og Mockito.

Når du skriver enhetstester, er det ofte nyttig å omgå innkapsling, og derfor inkluderer PowerMock flere funksjoner som forenkler refleksjon spesielt nyttig for testing. Dette gir enkel tilgang til intern tilstand, men forenkler også delvis og privat hån. PowerMock gir en klasse kalt PowerMockito for å lage mock/objekt/klasse og starte verifisering, og forventninger, alt annet kan du fortsatt bruke Mockito til å sette opp og bekrefte forventning (f.eks. times() , anyInt() ). All bruk krever@RunWith(PowerMockRunner.class) og @PrepareForTest kommentert på klassenivå.

2. Hånende

Mocking er en måte å teste funksjonaliteten til en klasse på isolert. Hånte gjenstander gjør narr av den virkelige tjenesten. Et falskt objekt returnerer en dummy-data som tilsvarer noen dummy-inndata som er sendt til den.

For å forstå hvordan PowerMockito fungerer, må vi først se på noen av begrepene vi bruker når vi bruker disse rammeverkene.

En stubb klasse er en delvis implementering for et grensesnitt eller klasse med det formål å bruke en forekomst av denne stubbklassen under testing. Stubber reagerer vanligvis i det hele tatt på alt utenfor det som er programmert inn for testen. Stubber kan også registrere informasjon om samtaler.

Et hånlig objekt er en dummy-implementering for et grensesnitt eller en klasse der du definerer utdata fra visse metodekall. Du kan lage disse mock-objektene manuelt (via kode) eller bruke et mock-rammeverk for å simulere disse klassene. Mock-rammeverk lar deg lage falske objekter under kjøring og definere oppførselen deres.

Nedenfor definerer vi noen få forskjeller mellom Mockito og PowerMock

  • Mockito inkluderer ikke spesifikke språkegenskaper som konstruktører eller statiske metoder for hån, mens PowerMock tilbyr konstruktører og statiske metoder til Mockito og andre rammeverk, gjennom sin individuelle klasselaster og bytekodeadministrasjon.
  • Mockito krever ikke @RunWith -annotering og basistestklasse, mens du utfører tester i suite, mens PowerMock krever både @RunWith -kommentar og en basistestklasse for å teste en suite.
  • Mockito støtter ikke hån av konstruktører, mens PowerMock støtter hån mot konstruktører og støtter også hån av (i) endelige (ii) statiske (iii) opprinnelige og (iv) private metoder.

PowerMock består av to utvidelses-API-er. En for EasyMock og en for Mockito. For å bruke PowerMock må du være avhengig av en av disse API-ene samt et testrammeverk. For øyeblikket støtter PowerMock JUnit og TestNG. Det er tre forskjellige JUnit-testutførere tilgjengelig, én for JUnit 4.4+, én for JUnit 4.0-4.3 og én for JUnit 3. Det er én testutøver for TestNG som krever versjon 5.11+ avhengig av hvilken versjon av PowerMock du bruker.

3. Opprette et prosjekt

Nedenfor er trinnene vi må ta for å lage prosjektet.

  • Åpne Eclipse. Gå til Fil=>Ny=>Java-prosjekt. I 'Prosjektnavn' skriver du inn 'PowerMockito'.

Figur 1. Lag Java-prosjekt

  • Eclipse vil opprette en «src»-mappe. Høyreklikk på 'src'-mappen og velg Ny => Pakke. Skriv inn "com.javacodegeeks" i tekstboksen "Navn". Klikk "Fullfør".

Figur 2. Java-pakke

3.1 Avhengigheter

For dette eksemplet trenger vi krukkene nedenfor:

  • cglib-nodep-3.2.2.jar
  • easymock-3.4.jar
  • hamcrest-all-1.3.jar
  • javassist-3.12.1.GA.jar
  • junit-4.12.jar
  • objenesis-2.2.jar
  • powermock-api-easymock-1.6.5.jar
  • powermock-mockito-release-full-1.6.4-full.jar

Disse glassene kan lastes ned fra Maven-depotet. Dette er de nyeste (ikke-beta) versjonene som er tilgjengelige per nå. For å legge til disse glassene i klassebanen, høyreklikk på prosjektet og velg Bygg bane=>Konfigurer byggebane. Klikk på "Legg til eksterne JARs"-knappen på høyre side. Gå deretter til stedet der du har lastet ned disse glassene. Klikk deretter ok.

4. Enkelt eksempel

Først vil vi se et enkelt eksempel på bruk av PowerMocito for å håne metoder. Vi vil opprette en Controller og en serviceklasse. Kontrollerklassen vil ha en referanse til tjenesteklassen som den skal bruke til å utføre brukerhandlinger.

4.1 Domeneklasse

Først vil vi se brukerdomeneklassen. Denne klassen representerer brukerenheten. (Vær oppmerksom på at den faktiske brukerenheten vil ha mange flere felt, men siden vi her viser hvordan du bruker PowerMock, bruker vi en veldig enkel brukerrepresentasjonsklasse.)

Bruker.java

package com.javacodegeeks.user.domain;

/**
* Class representing User entity.
* @author Meraj
*
*/
public class User {

  private String firstName;
  private String surname;

  public String getFirstName() {
    return firstName;
  }
  public void setFirstName(String firstName) {
    this.firstName = firstName;
  }
  public String getSurname() {
    return surname;
  }
  public void setSurname(String surname) {
    this.surname = surname;
  }
}

4.2 Brukertjeneste

Nå skal vi se hvordan brukerserviceklassen ser ut. Vi vil først opprette en klasse kalt DefaultUserService som vil implementere UserService grensesnitt. Her er vi interessert i getUserCount() metode som kaster UnsupportedOperationException .

DefaultUserService.java

package com.javacodegeeks.user.service;

import com.javacodegeeks.user.domain.User;

/**
* Default implementation of {@link UserService}
* @author Meraj
*
*/
public class DefaultUserService implements UserService {

  @Override
  public User getUserById(Long userId) {
    return null;
  }

  @Override
  public void updateUserDetails(User newUserDetails) {
  }

  @Override
  public void createUser(User user) {
  }

  @Override
  public Long getUserCount() {
    throw new UnsupportedOperationException("Not implemented");
  }
}

4.3 Brukerkontroller

Dette er kontrollerklassen for brukerrelaterte handlinger. Den har referansen til UserService-klassen som får sett når du initierer Controller-klassen ved å kalle dens konstruktør.

public UserController(UserService userService) {
  this.userService = userService;
}

UserController.java

package com.javacodegeeks.user.controller;

import com.javacodegeeks.user.service.UserService;

/**
* Controller class handling the user operations
* @author Meraj
*
*/
public class UserController {

  private UserService userService;

  public UserController(UserService userService) {
    this.userService = userService;
  }

  public Long getUserCount() {
    return userService.getUserCount();
  }
}

4.4 Test av brukerkontroller

Nå skal vi skrive testklassen for denne kontrollerklassen og se hvordan vi kan håne Service-klassen. Det er få ting som trenger litt oppmerksomhet her. Vi bruker @RunWith klasseanmerkning. Dette er definert i junit-biblioteket som vist nedenfor.

@Retention(value=RUNTIME)
@Target(value=TYPE)
@Inherited
public @interface RunWith

Når en klasse er merket med @RunWith eller utvider en klasse merket med @RunWith , vil JUnit påkalle klassen den refererer til for å kjøre testene i den klassen i stedet for løperen som er innebygd i JUnit.
Vi vil bruke org.powermock.modules.junit4.PowerMockRunner klasse for å kjøre denne testen. Vi vil håne DefaultUserService klasse ved å ringe mock() metode for org.powermock.api.mockito.PowerMockito .

PowerMockito.mock(DefaultUserService.class);

Vi sender dem denne falske referansen til UserController for å angi tjenesten.

UserControllerTest.java

package com.javacodegeeks.user.test;

import static org.junit.Assert.assertEquals;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import com.javacodegeeks.user.controller.UserController;
import com.javacodegeeks.user.service.DefaultUserService;

/**
* Test class for UserController
* @author Meraj
*
*/
@RunWith(PowerMockRunner.class)
@PrepareForTest(UserController.class)
public class UserControllerTest {

  private DefaultUserService mockUserService;
  private UserController userController;

  @Test
  public void testGetUserCount() {
    mockUserService = PowerMockito.mock(DefaultUserService.class);
    PowerMockito.when(mockUserService.getUserCount()).thenReturn(100L);
    userController = new UserController(mockUserService);
    assertEquals(100L, userController.getUserCount().longValue());
  }
}

5. Hånte statiske metoder

Vi kan bruke PowerMock til å håne statiske metoder. I denne delen vil vi se hvordan vi kan håne en statisk metode ved å bruke PowerMock. Vi bruker java.util.UUID klasse for dette. En UUID representerer en uforanderlig universelt unik identifikator (128 bit verdi). Flere detaljer om denne klassen finner du her:UUID Java Docs. I denne klassen er det en metode kalt randomUUID() . Den brukes til å hente en type 4 (pseudo tilfeldig generert) UUID. UUID-en genereres ved hjelp av en kryptografisk sterk pseudo-tilfeldig tallgenerator.

Vi vil lage en enkel metode i UserController klasse for å lage tilfeldig bruker-ID.

public String createUserId(User user) {
  return String.format("%s_%s", user.getSurname(), UUID.randomUUID().toString());
}

For å teste dette vil vi lage en ny testmetode i UserControllerTest klasse.

@Test
public void testMockStatic() throws Exception {
  PowerMock.mockStaticPartial(UUID.class, "randomUUID");
  EasyMock.expect(UUID.randomUUID()).andReturn(UUID.fromString("067e6162-3b6f-4ae2-a171-2470b63dff00"));
  PowerMock.replayAll();
  UserController userController = new UserController();
  Assert.assertTrue(userController.createUserId(getNewUser()).contains("067e6162-3b6f-4ae2-a171-2470b63dff00"));
  PowerMock.verifyAll();
}

Først ringer vi mockStaticPartial() metoden for org.powermock.api.easymock.PowerMock klasse som sender klassen og det statiske metodenavnet som streng som vi ønsker å håne:

PowerMock.mockStaticPartial(UUID.class, "randomUUID");

Deretter vil vi definere forventningen ved å kalle forventningsmetoden til EasyMock og returnere testverdien til den tilfeldige UUID-en.

EasyMock.expect(UUID.randomUUID()).andReturn(UUID.fromString("067e6162-3b6f-4ae2-a171-2470b63dff00"));

Nå kaller vi replayAll() metode for PowerMock .

PowerMock.replayAll();

Den spiller av alle klasser og falske objekter kjent av PowerMock. Dette inkluderer alle klasser som er klargjort for test ved hjelp av PrepareForTest eller PrepareOnlyThisForTest merknader og alle klasser som har fått sine statiske initialiseringer fjernet ved å bruke SuppressStaticInitializationFor merknad. Den inkluderer også alle falske forekomster opprettet av PowerMock, slik som de som er opprettet eller brukt av createMock(Class, Method...) , mockStatic(Class, Method...) , expectNew(Class, Object...) , createPartialMock(Class, String...) etc.

For å gjøre det enkelt å sende inn flere mocks som ikke er opprettet av PowerMock API, kan du spesifisere dem som additionalMocks. Dette er vanligvis de falske objektene du har laget med rene EasyMock- eller EasyMock-klasseutvidelser. Ingen ekstra hån trenger å spesifiseres hvis du bare bruker PowerMock API-metoder. EkstraMocks blir også automatisk verifisert når verifyAll() påkalles metode.

Nå kaller vi createUserId() metoden for UserController klasse ved å bestå testbrukerdetaljer.

Assert.assertTrue(userController.createUserId(getNewUser()).contains("067e6162-3b6f-4ae2-a171-2470b63dff00"));

Til slutt vil vi kalle verifyAll()

PowerMock.verifyAll();

Den verifiserer alle klasser og falske objekter kjent av PowerMock. Dette inkluderer alle klasser som er klargjort for test ved hjelp av PrepareForTest eller PrepareOnlyThisForTest merknader og alle klasser som har fått sine statiske initialiseringer fjernet ved å bruke SuppressStaticInitializationFor merknad. Den inkluderer også alle falske forekomster opprettet av PowerMock, slik som de som er opprettet eller brukt av createMock(Class, Method...) , mockStatic(Class, Method...) , expectNew(Class, Object...) , createPartialMock(Class, String...) osv.
Merk at alle tilleggsmodeller ble sendt til replayAll(Object...) metoden verifiseres også her automatisk.

6. Hånlige private metoder

I denne delen vil vi se hvordan vi kan håne en privat metode ved å bruke PowerMock. Det er en veldig nyttig funksjon som bruker java-refleksjon. Vi vil definere en offentlig metode i UserController-klassen som nedenfor:

public String getGreetingText(User user) {
  return String.format(getGreetingFormat(), user.getFirstName(), user.getSurname());
}

Som vi kan se at denne metoden kaller en privat metode getGreetingFormat() , som er definert som nedenfor:

private String getGreetingFormat() {
  return "Hello %s %s";
}

Vi vil prøve å endre oppførselen til denne private metoden ved å bruke PowerMock.

Du kan lage spioner av ekte gjenstander. Når du bruker spionen kalles de virkelige metodene (med mindre en metode ble stoppet). Spionering på virkelige objekter kan assosieres med "delvis hånende" konsept. Vi vil først lage en spion på UserController klasse.

UserController spy = spy(new UserController());

Deretter vil vi definere hvordan den private metoden skal oppføre seg når den kalles ved å bruke org.powermock.api.mockito.PowerMockito.when .

when(spy, method(UserController.class, "getGreetingFormat")).withNoArguments().thenReturn("Good Morning %s %s");

Nå vil vi kalle den offentlige metoden (som bruker den private metoden) på det spionerte objektet

assertEquals("Good Morning Code Geeks", spy.getGreetingText(user));

Nedenfor er kodebiten for hele metoden

@Test
public void testMockPrivateMethod() throws Exception {
  UserController spy = spy(new UserController());
  when(spy, method(UserController.class, "getGreetingFormat")).withNoArguments().thenReturn("Good Morning %s %s");
  User user = new User();
  user.setFirstName("Code");
  user.setSurname("Geeks");
  assertEquals("Good Morning Code Geeks", spy.getGreetingText(user));
}

7. Hånte sluttklasser

EasyMock lar deg ikke håne en siste klasse, men PowerMock gjør det. Du kan enkelt gjøre

ExampleFinalClass clazz =PowerMock.createMock(ExampleFinalClass .class);

Nedenfor er listen over dette som vi må gjøre for å håne en siste klasse:

  1. Bruk @RunWith(PowerMockRunner.class)-kommentaren på klassenivå i testsaken.
  2. Bruk @PrepareForTest(ClassWithFinal.class)-kommentaren på klassenivå i testsaken.
  3. Bruk PowerMock.createMock(FinalClazz.class) for å lage et mock-objekt for alle metodene i denne klassen (la oss kalle det mockObject).
  4. Bruk PowerMock.replay(mockObject) for å endre det falske objektet til replay-modus.
  5. Bruk PowerMock.verify(mockObject) for å endre det falske objektet til verifiseringsmodus.

Først skal vi lage en veldig enkel sluttklasse med bare en siste metode.

SimpleFinalClazz.java

package com.javacodegeeks.powermock;

public final class SimpleFinalClazz {

  public final String simpleFinalMethod() {
    return "Final String";
  }

}

Nå skal vi definere en annen klasse som skal bruke denne klassen.

FinalClazzUser.java

package com.javacodegeeks.powermock;

public class FinalClazzUser {

  private SimpleFinalClazz simpleFinalClazz;

  public FinalClazzUser(SimpleFinalClazz simpleFinalClazz) {
    this.simpleFinalClazz = simpleFinalClazz;
  }

  public String getMeSomething() {
    return "Get Me Something " + simpleFinalClazz.simpleFinalMethod();
  }

}

Nå skal vi se hvordan vi kan håne sluttklassen SimpleFinalClazz for å endre oppførselen.

@Test
public void testMockFinal() {
  SimpleFinalClazz simpleFinalClazz = PowerMock.createMock(SimpleFinalClazz.class);
  FinalClazzUser finalClazzUser = new FinalClazzUser(simpleFinalClazz);
  EasyMock.expect(simpleFinalClazz.simpleFinalMethod()).andReturn("Hurray!!!");
  PowerMock.replay(simpleFinalClazz);
  String actual = finalClazzUser.getMeSomething();
  PowerMock.verify(simpleFinalClazz);
  Assert.assertEquals(actual, "Get Me Something Hurray!!!");
}

Først kaller vi createMock()-metoden til PowerMock og sender SimpleFinalClazz-referansen som parameteren for å spotte den.

SimpleFinalClazz simpleFinalClazz = PowerMock.createMock(SimpleFinalClazz.class);

Deretter oppretter vi forekomsten av FinalClazzUser-klassen ved å sende det falske objektet opprettet ovenfor.

FinalClazzUser finalClazzUser = new FinalClazzUser(simpleFinalClazz);

Deretter vil vi definere forventningen til metoden definert i den siste klassen.

EasyMock.expect(simpleFinalClazz.simpleFinalMethod()).andReturn("Hurray!!!");

Nå skal vi spille av det falske objektet.

PowerMock.replay(simpleFinalClazz);

Nå skal vi bekrefte forventet og faktisk oppførsel

PowerMock.verify(simpleFinalClazz);
Assert.assertEquals(actual, "Get Me Something Hurray!!!");

8. Mock konstruktør

Et av de største problemene med å skrive enhetstester er den uskyldige bruken av «nye» – dvs. konstruktører. Når klassen du vil teste i sin tur eksplisitt konstruerer avhengighetene sine, kan du ha et stort problem. I denne delen vil vi se hvordan vi kan bruke PowerMockito til å håne konstruktører.

Først vil vi definere en enkel klasse SimpleClass med én metode getMeCurrentDateAsString()

package com.javacodegeeks.powermock.constructor;

import java.util.Calendar;

public class SimpleClass {

  @SuppressWarnings("deprecation")
  public String getMeCurrentDateAsString() {
  return Calendar.getInstance().getTime().toGMTString();

  }

}

Nå skal vi definere en annen klasse PowerMockConstructorExample som har referansen til den første klassen (SimpleClass)

package com.javacodegeeks.powermock.constructor;

public class PowerMockConstructorExample {

public String getMeSimpleObject() {
  SimpleClass simpleClass = new SimpleClass();

  String returnValue = simpleClass.getMeCurrentDateAsString();
  return returnValue;
  }

}

Nå skal vi se hvordan vi kan håne SimpleClass klasse.

Merk SimpleClass-referansen med @Mock merknad

@Mock private SimpleClass mockSimpleClass;

Bruk nå expectNew() metoden til PowerMock for å angi mock-klassen i stedet for den ekte. Det gjør det mulig å spesifisere forventninger til nye påkallelser. Merk at du må spille av klassen på nytt når du bruker denne metoden, siden denne oppførselen er en del av klassens mock.

expectNew(SimpleClass.class).andReturn(mockSimpleClass);

Nå skal vi definere metodeforventningen som nedenfor

expect(mockSimpleClass.getMeCurrentDateAsString()).andReturn("Mock Result");

Nå skal vi spille den falske klassen på nytt.

replay(SimpleClass.class, mockSimpleClass);

Nå bekrefter vi resultatet

String value = instance.getMeSimpleObject();
verify(SimpleClass.class, mockSimpleClass);
assertEquals("Mock Result", value);

Nedenfor er hele testklassen som vi brukte for å teste dette:

PowerMockConstructorExampleTest.java

package com.javacodegeeks.powermock.constructor;

import static org.easymock.EasyMock.expect;
import static org.powermock.api.easymock.PowerMock.expectNew;
import static org.powermock.api.easymock.PowerMock.replay;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.easymock.annotation.Mock;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import static org.powermock.api.easymock.PowerMock.verify;
import static org.junit.Assert.assertEquals;

@RunWith(PowerMockRunner.class)
@PrepareForTest(PowerMockConstructorExample.class)
public class PowerMockConstructorExampleTest {

  @Mock private SimpleClass mockSimpleClass;

  private PowerMockConstructorExample instance;

  @Test
  public void testMockConstructor() throws Exception {
    instance = new PowerMockConstructorExample();
    expectNew(SimpleClass.class).andReturn(mockSimpleClass);

    expect(mockSimpleClass.getMeCurrentDateAsString()).andReturn("Mock Result");

    replay(SimpleClass.class, mockSimpleClass);
    String value = instance.getMeSimpleObject();
    verify(SimpleClass.class, mockSimpleClass);
    assertEquals("Mock Result", value);
  }

}

9. Last ned kildefilen

I denne artikkelen så vi bruken av PowerMock og hvordan den gir ekstra funksjoner som Mockito/EasyMock ikke tilbyr.

Java Tag