2017-09-13 1 views
0

Ich habe kürzlich gelernt, PowerMock zu verwenden, um Komponententests für eine Klasse namens Module zu schreiben, die die Klasse Base erweitert. Sie sehen so aus.Mit PowerMock zum Schreiben von Komponententests kann die Mock-Methode nicht aufgerufen werden

public class Base { 
    protected final static ServiceA serviceA; 
    protected final static ServiceB serviceB; 
    static { 
     serviceA = ServiceA.getInstance(); 
     serviceB = ServiceB.getInstance(); 
    } 
} 

public class Module extends Base { 
    public DataA methodA() { 
     return serviceA.getDataA(); 
    } 
    public DataB methodB() { 
     return serviceB.getDataB(); 
    } 
} 

My Unit-Tests wie folgt aussehen:

@PowerMockIgnore("javax.management.*") 
@RunWith(PowerMockRunner.class) 
@PrepareForTest({Module.class, ServiceA.class, ServiceB.class}) 
public class ModuleTest { 
    private Module module; 
    @Mock 
    private ServiceA serviceA; 
    @Mock 
    private ServiceB serviceB; 

    @Before 
    public void setup() throws Exception { 
     MockitoAnnotations.initMocks(this); 

     PowerMockito.mockStatic(ServiceA.class); 
     PowerMockito.when(ServiceA.getInstance).thenReturn(serviceA); 

     PowerMockito.mockStatic(ServiceB.class); 
     PowerMockito.when(ServiceB.getInstance).thenReturn(serviceB); 

     module = new Module(); 
     // I spy it because it has other methods I need to mock 
     module = PowerMockito.spy(module); 
    } 

    @Test 
    public void methodATest() { 
     DataA dataA = new DataA(); 
     PowerMockito.when(serviceA.getDataA()).thenReturn(dataA); 
     DataA data = module.methodA(); 
     assertEquals(dataA, data); 
    } 
    @Test 
    public void methodBTest() { 
     DataB dataB = new DataB(); 
     PowerMockito.when(serviceB.getDataB()).thenReturn(dataB); 
     DataB data = module.methodB(); 
     assertEquals(dataB, data); 
    } 
} 

Alles sieht einfach, aber wenn ich ModuleTest ausführen, wird das methodBTest() nicht passieren. Es scheint, dass PowerMockito.when(serviceB.getDataB()).thenReturn(dataB) nicht funktioniert und die echte serviceB.getDataB() Methode aufgerufen wird. Also assertEquals(dataB, data) wirft org.junit.ComparisonFailure.

Wenn ich die methodBTest() vor methodATest() setzen, wird die methodATest() nicht bestanden. Gleicher Grund. Wenn ich PowerMockito.when(serviceA.getDataA()).thenReturn(dataA) und PowerMockito.when(serviceB.getDataB()).thenReturn(dataB) in das Setup() setze, funktioniert alles perfekt.

Das grenzt mich den ganzen Tag. Weiß jemand, warum dies geschieht und wie man es löst? Ich brauche die spöttische Aussage in den jeweiligen Testmethoden geschrieben, weil ich die zurückgegebenen Werte ändern kann.

+2

Ich würde Ihnen vorschlagen PowerMock zu entfernen und wie etwas zu verwenden: 'public DataA methodA() { return methodA (ServiceA.getInstance()); }/* zu Testzwecken */protected DataA methodA (DienstA serviceA) { return serviceA.getDataA(); } –

+0

@RC. Vielen Dank. Das könnte eine Lösung sein, aber eigentlich wurde ich gebeten, zu versuchen, die ursprüngliche Klasse nicht zu ändern. – IntoCode

Antwort

2

Hier ist eine Lösung beteiligt (fast) keine Änderungen

@PowerMockIgnore("javax.management.*") 
@RunWith(PowerMockRunner.class) 
@PrepareForTest({Module.class, ServiceA.class, ServiceB.class}) 
public class ModuleTest { 
    private Module module; 

    private static ServiceA serviceA = Mockito.mock(ServiceA.class); 

    private static ServiceB serviceB = Mockito.mock(ServiceB.class); 

    @BeforeClass 
    public static void oneTimeSetup() throws Exception { 
     PowerMockito.mockStatic(ServiceA.class); 
     PowerMockito.when(ServiceA.class, "getInstance").thenReturn(serviceA); 

     PowerMockito.mockStatic(ServiceB.class); 
     PowerMockito.when(ServiceB.class, "getInstance").thenReturn(serviceB); 
    } 

    @Before 
    public void setup() throws Exception { 
     module = new Module(); 
     // I spy it because it has other methods I need to mock 
     module = PowerMockito.spy(module); 
    } 

    @Test 
    public void methodATest() { 
     DataA dataA = new DataA(); 
     Mockito.when(serviceA.getDataA()).thenReturn(dataA); 
     DataA data = module.methodA(); 
     assertEquals(dataA, data); 
    } 
    @Test 
    public void methodBTest() { 
     DataB dataB = new DataB(); 
     Mockito.when(serviceB.getDataB()).thenReturn(dataB); 
     DataB data = module.methodB(); 
     assertEquals(dataB, data); 
    } 
} 

Was geändert wurde (und warum):

  • In Base: serviceA und serviceB sind geschützt geändert (Module kann nicht zugreifen, wenn privat)
  • verwendet "richtig" (AFAIK) Syntax für PowerMockito.when(ServiceA.class, "getInstance").thenReturn(serviceA);
  • verwendet, um ein @BeforeClass und machte serviceA und serviceB statisch auf "Bypass" statische Initialisierung in Base

Getestet mit Junit 4,12, PowerMockito 1.6.2.


Hinweis: Es ist auch möglich, @SuppressStaticInitializationFor zu nutzen, um das gleiche Ziel zu erreichen:

@SuppressStaticInitializationFor(value = "so46196071.Base") // suppress the static in Base (note this is my package name) 
@PowerMockIgnore("javax.management.*") 
@RunWith(PowerMockRunner.class) 
@PrepareForTest({Module.class, ServiceA.class, ServiceB.class}) 
public class ModuleBisTest { 
    private Module module; 

    @Mock 
    private ServiceA serviceA; 

    @Mock 
    private ServiceB serviceB; 

    @Before 
    public void setup() throws Exception { 
     // MockitoAnnotations.initMocks(this); /* this is not needed => done by the runner */ 

     PowerMockito.mockStatic(ServiceA.class); 
     PowerMockito.when(ServiceA.class, "getInstance").thenReturn(serviceA); 

     PowerMockito.mockStatic(ServiceB.class); 
     PowerMockito.when(ServiceB.class, "getInstance").thenReturn(serviceB); 

     module = new Module(); 
     Whitebox.setInternalState(Base.class, "serviceA", serviceA); // set serviceA in Base "by hand" 
     Whitebox.setInternalState(Base.class, "serviceB", serviceB); // set serviceB in Base "by hand" 
     // I spy it because it has other methods I need to mock 
     module = PowerMockito.spy(module); 
    } 

    // ... 
+0

Vielen Dank. Ich habe beide Lösungen ausprobiert und sie funktionieren perfekt! Ich entscheide mich für die zweite Lösung. :) Und ja, das Datenfeld 'Service' ist in der ursprünglichen 'Base'-Klasse' geschützt '. Es war mein Fehler. – IntoCode

Verwandte Themen