Archiv für den Monat: November 2011

Mocken von privaten Methoden mit PowerMock

Nachdem in den bisherigen drei Teilen um das Mocken von Konstruktoren, statischen Methoden und den Zugriff auf private Variablen ging, soll es in diesem Teil um das mocken von privaten Methoden gehen. Zunächst brauchen wir eine Motivation, private Methoden überhaupt mocken zu wollen.

In dem nachfolgenden Beispiel werden über eine public-Methode zwei weitere private Methoden aufgerufen, die z.B. aufwendige Vorbereitungen bedürfen oder eine sehr lange Ausführungszeit haben.

package de.sevendroids.java.powermock.sample;

public class PowerMockSamplePrivateMethod {

  public String createSpecialString(String in) {
    StringBuilder sb = new StringBuilder();

    if (in  != null && !in.isEmpty())
      sb.append(createNotEmptyString(in));
    if (in.length() == 9)
      sb.append(create9String(in));
    if (in.length()<20)
      sb.append(createLess20String(in));

    return sb.toString();
  }

  private String createNotEmptyString(String in) {
    String start = "The in String is not empty. ";

    if (in.trim().isEmpty())
      return start + "But after trim() it is empty. ";
    return start;
  }

  private String createLess20String(String in) {
    // Very complicated conditions
    // Hard to generate objects
    return null;
  }

  private String create9String(String in) {
    // Very complicated conditions
    // Hard to generate objects
    return null;
  }
}

Damit ein Unittest in einer angemessenen Zeit ausgeführt werden kann, müssen die lang laufenden Methoden gemockt werden können. Mittels PowerMock, EasyMock und JUnit/TestNG ist dieses möglich.

Hier ein Beispiel mit TestNG, EasMock und PowerMock:

import static org.testng.Assert.assertEquals;
import static org.powermock.api.easymock.PowerMock.createPartialMock;
import static org.powermock.api.easymock.PowerMock.expectPrivate;
import static org.powermock.api.easymock.PowerMock.replay;
import static org.powermock.api.easymock.PowerMock.verify;

import org.powermock.core.classloader.annotations.PrepareForTest;
import org.testng.annotations.Test;

/**
 * @author 7droids.de (FA) <br>
 *         Sample class with use cases for the mocking lib Power Mock
 *
 */
@PrepareForTest(PowerMockSamplePrivateMethod.class) // 1
public class PowerMockSamplePrivateMethodTest {

  /**
   * Test method for
   * {@link PowerMockSamplePrivateMethod#createSpecialString(String)}.
   *
   * @throws Exception
   */
  @Test
  public final void testCreateSpecialString() throws Exception {
    final String mockMethodCreateLess20String = "createLess20String";
    final String mockMethodCreate9String = "create9String";
    final String in = "9-String ";

    PowerMockSamplePrivateMethod instance = createPartialMock(
          PowerMockSamplePrivateMethod.class,
          mockMethodCreateLess20String, mockMethodCreate9String); // 2

    expectPrivate(instance, mockMethodCreateLess20String, in).andReturn(
          "Dummy for < 20. "); // 3
    expectPrivate(instance, mockMethodCreate9String, in).andReturn(
          "Dummy for = 9. "); // 4

    replay(instance); // 5
    assertEquals(
          "The in String is not empty. Dummy for = 9. Dummy for < 20. ",
          instance.createSpecialString(in));
    verify(instance);
  }
}

In Zeile 1 wird PowerMock die zu mockende Klasse mitgeteilt. Zeile 2 macht PowerMock die Methoden bekannt, die gemockt werden sollen. In den Zeilen 3 und 4 wird dem Mocking-Framework mit geteilt, dass die gemockten Methoden aufgerufen werden sollen und dass stattdessen ein anderer String zurückgegeben werden soll. Replay() in Zeile 5 macht EasyMock bereit für den eigentlichen Test. Alles Folgende ist bekannte Vorgehensweise bei TestNG und EasyMock.

Zu beachten ist, dass es die Möglichkeit private Methoden mit PowerMock zu mocken erst ab der Version 1.4.x gibt.