Powermock ist ein Framework, dass mir vor allem da hilft, wo die Hilfe von Mockito aufhört.

Beim Mocking von abstract, final und/oder static Methoden oder Variablen gibt es mit Mockito keine Lösung, da Mockito überwiegend mit Vererbung der zu „mock“enden Klassen vorgeht.

Ich möchte jetzt NICHT hören: Wer schreibt denn heute noch static Methoden, wo es Spring gibt?
Das ist mir auch klar, aber zahlreiche APIs (insbesondere das JDK selbst) sehen das anders und deshalb benötigt man für deren Verwendung eben Powermock.

Wie Powermock funktioniert, lest bitte hier nach.
Hier kümmere ich mich um ein sehr spezielles Problem, das neulich in meinem Projekt aufgetaucht ist.

Wir hatten einen Unittest für eine Klasse, der sowohl in Eclipse als auch bei der Ausführung von mvn test -Dtest=de.mike.test.MeinTest grün war.
Wurde jedoch der gleiche Test durch mvn test oder auf dem Buildserver Bamboo ausgeführt, gab es folgende Exception:

...java.lang.UnsatisfiedLinkError: Could not load library. Reasons: [no jansi-1.5 in java.library.path, no jansi in java.library.path,</pre>
<pre>Native Library C:\Users\albrecht\AppData\Local\Temp\jansi-1.5.dll already loaded in another classloader]
        at org.fusesource.hawtjni.runtime.Library.doLoad(Library.java:182)
        at org.fusesource.hawtjni.runtime.Library.load(Library.java:142)
        at org.fusesource.jansi.internal.Kernel32.<clinit>(Kernel32.java:40)
        at org.fusesource.jansi.WindowsAnsiOutputStream.<clinit>(WindowsAnsiOutputStream.java:52)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:408)
        at org.apache.logging.log4j.core.appender.ConsoleAppender.getOutputStream(ConsoleAppender.java:198)
        at org.apache.logging.log4j.core.appender.ConsoleAppender.getManager(ConsoleAppender.java:175)
        at org.apache.logging.log4j.core.appender.ConsoleAppender.createDefaultAppenderForLayout(ConsoleAppender.java:106)
        at org.apache.logging.log4j.core.config.DefaultConfiguration.<init>(DefaultConfiguration.java:62)
        at org.apache.logging.log4j.core.LoggerContext.<init>(LoggerContext.java:70)
        at org.apache.logging.log4j.core.selector.ClassLoaderContextSelector.locateContext(ClassLoaderContextSelector.java:145)
        at org.apache.logging.log4j.core.selector.ClassLoaderContextSelector.getContext(ClassLoaderContextSelector.java:70)
        at org.apache.logging.log4j.core.selector.ClassLoaderContextSelector.getContext(ClassLoaderContextSelector.java:57)
        at org.apache.logging.log4j.core.impl.Log4jContextFactory.getContext(Log4jContextFactory.java:142)
        at org.apache.logging.log4j.core.impl.Log4jContextFactory.getContext(Log4jContextFactory.java:41)
        at org.apache.logging.log4j.LogManager.getContext(LogManager.java:175)
        at org.apache.logging.log4j.spi.AbstractLoggerAdapter.getContext(AbstractLoggerAdapter.java:102)
        at org.apache.logging.slf4j.Log4jLoggerFactory.getContext(Log4jLoggerFactory.java:43)
        at org.apache.logging.log4j.spi.AbstractLoggerAdapter.getLogger(AbstractLoggerAdapter.java:42)
        at org.apache.logging.slf4j.Log4jLoggerFactory.getLogger(Log4jLoggerFactory.java:29)
        at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:285)
        at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:305)
        at de.mike.test.MeinTest.<clinit>(MeinTest.java:34)

Running de.mike.test.MeinTest
...

Wie anhand des Logs deutlich wird, liegt das Problem in der Verwendung der dynamischen Bibliothek jansi-1.5.dll durch das slf4j Framework.

In irgendeinem anderen meiner zahlreichen Tests wird diese dll bereits geladen und deshalb kann sie an dieser Stelle durch Powermock nicht nochmals geladen werden, was zu einem UnsatisfiedLinkError zur Laufzeit führt.

Nun könnte ich die Verwendung des Loggers durch Powermock erneut mocken und damit das Laden der dll verhindern oder ich verhindere, weil ich es nicht brauche, komplett das Laden aller org.slf4j.* Klassen durch Einfügen der folgenden hinterlegten Annotation in meinem Test:

@RunWith(PowerMockRunner.class)
@PrepareForTest({ ZuMockendeKlasseMitStaticMethode.class })
@PowerMockIgnore({ "org.slf4j.*" })
public class MeinTest {
   ...
}

Mit PowerMockIgnore kann seit Powermock Version 1.3.5 das Laden der angegebenen Ressourcen verhindert werden. Das macht natürlich nur Sinn, bei allen Komponenten, die man NICHT verifziert, sicherstellen oder testen möchte. Klassischerweise gehört das Logging beispielsweise dazu.

 

PowerMockIgnore
Markiert in:            

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.