「テストをEclipse上で実行してOKが出たら、カバレッジを取るためにMavenでテストを実行」てな感じで作業を進めてるのですが、Maven実行時に下記のような例外が発生することがあります。
java.lang.UnsatisfiedLinkError: Native Library C:\Java\jdk1.8.0_144_x64\jre\bin\jpeg.dll already loaded in another classloader
  at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1907)
  at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1845)
  at java.lang.Runtime.loadLibrary0(Runtime.java:870)
  at java.lang.System.loadLibrary(System.java:1122)
  at com.sun.imageio.plugins.jpeg.JPEGImageWriter$1.run(JPEGImageWriter.java:180)
  at com.sun.imageio.plugins.jpeg.JPEGImageWriter$1.run(JPEGImageWriter.java:178)
  at java.security.AccessController.doPrivileged(Native Method)
  at com.sun.imageio.plugins.jpeg.JPEGImageWriter.(JPEGImageWriter.java:177)
  at com.sun.imageio.plugins.jpeg.JPEGImageWriterSpi.createWriterInstance(JPEGImageWriterSpi.java:96)
  at javax.imageio.spi.ImageWriterSpi.createWriterInstance(ImageWriterSpi.java:351)
  at javax.imageio.ImageIO$ImageWriterIterator.next(ImageIO.java:843)
  at javax.imageio.ImageIO$ImageWriterIterator.next(ImageIO.java:827)
  at javax.imageio.ImageIO.getWriter(ImageIO.java:1596)
  at javax.imageio.ImageIO.write(ImageIO.java:1520)
  ...
試している限りでは次のような条件で発生するようです。
  1. 複数のテストクラスでImageIOを利用するクラスが呼ばれる
  2. テスト開始後、2つ目のテストクラス実行時に上記例外が発生する
UnsatisfiedLinkErrorが発生した後、同テストクラス内の他のImageIOを利用するクラスを利用するテストケースでは、次の例外が発生します。
java.lang.NoClassDefFoundError: Could not initialize class com.sun.imageio.plugins.jpeg.JPEGImageWriter
  at com.sun.imageio.plugins.jpeg.JPEGImageWriterSpi.createWriterInstance(JPEGImageWriterSpi.java:96)
  at javax.imageio.spi.ImageWriterSpi.createWriterInstance(ImageWriterSpi.java:351)
  at javax.imageio.ImageIO$ImageWriterIterator.next(ImageIO.java:843)
  at javax.imageio.ImageIO$ImageWriterIterator.next(ImageIO.java:827)
  at javax.imageio.ImageIO.getWriter(ImageIO.java:1596)
  at javax.imageio.ImageIO.write(ImageIO.java:1520)
  ...
目的のクラスが読み込まれてないので、NoClassDefFoundErrorが発生するのも当然ですね。
なお、環境は次の通りです。
  • Java SE Development Kit 8u144 (ImageIOを含む)
  • Apache Maven 3.5.0
  • PowerMock 1.7.3

原因はPowerMockのクラスローダー

調べたところ、事象はちょっと違うのですが、次の記事が見つかりました。 解決策としては、テストクラスに次のアノテーションを追加すると良いそうです。
@PowerMockIgnore({"javax.imageio.*", "javax.security.*"})
発生する原因は次のような理由みたいです。
  1. PowerMockを利用するクラスでは、デフォルトのクラスローダーではなくPowerMockが用意するクラスローダーが使われる。
  2. ImageIOはImageIOが持つクラスローダーで別のクラスをロードする。
  3. 2つ目のテストクラス実行時、再度PowerMockによりImageIOが読み込まれるが、その際にImageIOのクラスローダーが再度別クラスを読み込みに行ってしまう。
  4. この現象はPowerMockを利用したテストクラスでのみ起こるため、それであればPowerMockのクラスローダーでImageIO関連のクラスを読み込まなければ良い。
よって、上記アノテーションを付けると、ImageIO関連のクラスがPowerMockの対象外となるため、UnsatisfiedLinkErrorは発生しなくなるとのこと。今回はImageIOが該当しましたが、JNAみたくOSネイティブのライブラリを読み込むところでは同様の問題が発生する可能性がありそうです。

JAI ImageIOを利用している場合

Tiff形式などImageIOがサポートしていない形式を扱う場合、JAI ImageIOを利用するかと思います。
  • jai-imageio-core 1.3.1
JAI ImageIOを利用しているときに上記アノテーションを付けると、次の例外が発生します。
java.lang.LinkageError: loader constraint violation: when resolving overridden method "com.github.jaiimageio.impl.plugins.tiff.TIFFStreamMetadata.mergeTree(Ljava/lang/String;Lorg/w3c/dom/Node;)V" the class loader (instance of org/powermock/core/classloader/MockClassLoader) of the current class, com/github/jaiimageio/impl/plugins/tiff/TIFFStreamMetadata, and its superclass loader (instance of ), have different Class objects for the type org/w3c/dom/Node used in the signature
  at com.github.jaiimageio.impl.plugins.tiff.TIFFImageWriter.getDefaultStreamMetadata(TIFFImageWriter.java:336)
  at com.github.jaiimageio.impl.plugins.tiff.TIFFImageWriter.write(TIFFImageWriter.java:2522)
  at com.github.jaiimageio.impl.plugins.tiff.TIFFImageWriter.write(TIFFImageWriter.java:2383)
  at javax.imageio.ImageWriter.write(ImageWriter.java:615)
  at javax.imageio.ImageIO.doWrite(ImageIO.java:1612)
  at javax.imageio.ImageIO.write(ImageIO.java:1536)
  ...
これもPowerMockのクラスローダーが原因のようなので、@PowerMockIgnoreに次のクラスを追加してください。
com.github.jaiimageio.*
これでLinkageErrorも解消します。