Powermock imageio UnsatisfiedLinkError
Powermock imageio UnsatisfiedLinkError
将 powermock(1.5.6 与 Easymock 3.2 结合)添加到我当前的项目(jdk 1.6.0)后,我在测试方法中遇到了一些测试失败,而这些测试方法之前工作得很好:
java.lang.UnsatisfiedLinkError:
com.sun.imageio.plugins.jpeg.JPEGImageReader.initReaderIDs(Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/Class;)V
以下代码失败:
BufferedImage img = null;
try {
img = ImageIO.read(this.getClass().getResourceAsStream("/example.jpg"));
}
catch (IOException e) {
fail(e.getMessage());
}
powermock 页面已经有一个 bug 从 2009 年开始,但没有修复也没有解决方法。 (回到 32 位是无稽之谈,因为这些方法在没有 powermock 的情况下也能工作)所以有人知道如何解决这个问题吗?
更新 I: 切换到 32 位是没有选择的,除此之外这不是问题所在。如果我不使用 PowerMock,每个测试在我的 64 位 JVM 中都能完美运行...
更新二:好的,这是请求的信息
更新三:扩展了class
Class待测
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.cert.X509Certificate;
import javax.imageio.ImageIO;
import sun.security.x509.CertificateIssuerName;
import sun.security.x509.CertificateSubjectName;
import sun.security.x509.X500Name;
import sun.security.x509.X509CertImpl;
import sun.security.x509.X509CertInfo;
public class App {
private X509Certificate certificate = null;
public ByteArrayOutputStream readImage() {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
BufferedImage img = null;
try {
img = ImageIO.read(this.getClass().getResourceAsStream("/example.jpg"));
ImageIO.write(img, "png", baos);
}
catch (IOException e) {
e.printStackTrace();
}
return baos;
}
public String readCertificate() throws Exception{
this.certificate = generateCertificate();
return this.certificate.getIssuerX500Principal().getName();
}
private static X509Certificate generateCertificate() throws GeneralSecurityException, IOException{
X509CertInfo info = new X509CertInfo();
X500Name owner = new X500Name("CN=example.net");
info.set(X509CertInfo.SUBJECT, new CertificateSubjectName(owner));
info.set(X509CertInfo.ISSUER, new CertificateIssuerName(owner));
return new X509CertImpl(info);
}
}
测试用例:
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
@RunWith(PowerMockRunner.class)
@PrepareForTest(App.class)
@PowerMockIgnore("javax.imageio.*, javax.security.*")
public class AppTest {
@Test
public void testApp(){
App test = new App();
Assert.assertNotNull(test.readImage());
Assert.assertEquals(284506, test.readImage().size());
}
@Test
public void testCertificate() throws Exception{
App test = new App();
test.readCertificate();
}
}
Maven 依赖项:
<dependencies>
<!-- TEST -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
<version>3.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>1.5.6</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-easymock</artifactId>
<version>1.5.6</version>
<scope>test</scope>
</dependency>
因此,如果您注释该行:
//@RunWith(PowerMockRunner.class)
这是工作。如果取消注释,则会抛出上述错误(再次!)
解决方案是告诉 PowerMock 忽略所有与其自定义 class 加载程序冲突的 JRE classes。
即在测试中添加如下注解class:
@PowerMockIgnore({"javax.imageio.*", "javax.security.*"})
(请注意注释的 value
属性采用正则表达式的 数组 ;它不支持单个字符串中的多个逗号分隔表达式。)
为什么需要这个的解释是
- PowerMock 通过在其自己的自定义 classloader 中重新加载准备好的 class(以及测试 class)来运行;
- 当
App
调用 javax.imageio.ImageIO
时,它最终会尝试加载和初始化内部 class com.sun.imageio.plugins.jpeg.JPEGImageReader
,然后它会尝试加载其他一些 com.sun.imageio
classes 来自 caller class 加载程序;
- class 加载失败,因为 PowerMock 的自定义 class 加载器显然找不到那些 JRE classes(很难说清楚此时发生了什么,因为加载由
JPEGImageReader
class 中的本机方法完成 - 也许它也尝试加载一些本机库)。
将 powermock(1.5.6 与 Easymock 3.2 结合)添加到我当前的项目(jdk 1.6.0)后,我在测试方法中遇到了一些测试失败,而这些测试方法之前工作得很好:
java.lang.UnsatisfiedLinkError: com.sun.imageio.plugins.jpeg.JPEGImageReader.initReaderIDs(Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/Class;)V
以下代码失败:
BufferedImage img = null;
try {
img = ImageIO.read(this.getClass().getResourceAsStream("/example.jpg"));
}
catch (IOException e) {
fail(e.getMessage());
}
powermock 页面已经有一个 bug 从 2009 年开始,但没有修复也没有解决方法。 (回到 32 位是无稽之谈,因为这些方法在没有 powermock 的情况下也能工作)所以有人知道如何解决这个问题吗?
更新 I: 切换到 32 位是没有选择的,除此之外这不是问题所在。如果我不使用 PowerMock,每个测试在我的 64 位 JVM 中都能完美运行...
更新二:好的,这是请求的信息
更新三:扩展了class
Class待测
import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.security.GeneralSecurityException; import java.security.cert.X509Certificate; import javax.imageio.ImageIO; import sun.security.x509.CertificateIssuerName; import sun.security.x509.CertificateSubjectName; import sun.security.x509.X500Name; import sun.security.x509.X509CertImpl; import sun.security.x509.X509CertInfo; public class App { private X509Certificate certificate = null; public ByteArrayOutputStream readImage() { ByteArrayOutputStream baos = new ByteArrayOutputStream(); BufferedImage img = null; try { img = ImageIO.read(this.getClass().getResourceAsStream("/example.jpg")); ImageIO.write(img, "png", baos); } catch (IOException e) { e.printStackTrace(); } return baos; } public String readCertificate() throws Exception{ this.certificate = generateCertificate(); return this.certificate.getIssuerX500Principal().getName(); } private static X509Certificate generateCertificate() throws GeneralSecurityException, IOException{ X509CertInfo info = new X509CertInfo(); X500Name owner = new X500Name("CN=example.net"); info.set(X509CertInfo.SUBJECT, new CertificateSubjectName(owner)); info.set(X509CertInfo.ISSUER, new CertificateIssuerName(owner)); return new X509CertImpl(info); } }
测试用例:
import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.powermock.core.classloader.annotations.PowerMockIgnore; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; @RunWith(PowerMockRunner.class) @PrepareForTest(App.class) @PowerMockIgnore("javax.imageio.*, javax.security.*") public class AppTest { @Test public void testApp(){ App test = new App(); Assert.assertNotNull(test.readImage()); Assert.assertEquals(284506, test.readImage().size()); } @Test public void testCertificate() throws Exception{ App test = new App(); test.readCertificate(); } }
Maven 依赖项:
<dependencies> <!-- TEST --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <dependency> <groupId>org.easymock</groupId> <artifactId>easymock</artifactId> <version>3.2</version> <scope>test</scope> </dependency> <dependency> <groupId>org.powermock</groupId> <artifactId>powermock-module-junit4</artifactId> <version>1.5.6</version> <scope>test</scope> </dependency> <dependency> <groupId>org.powermock</groupId> <artifactId>powermock-api-easymock</artifactId> <version>1.5.6</version> <scope>test</scope> </dependency>
因此,如果您注释该行:
//@RunWith(PowerMockRunner.class)
这是工作。如果取消注释,则会抛出上述错误(再次!)
解决方案是告诉 PowerMock 忽略所有与其自定义 class 加载程序冲突的 JRE classes。 即在测试中添加如下注解class:
@PowerMockIgnore({"javax.imageio.*", "javax.security.*"})
(请注意注释的 value
属性采用正则表达式的 数组 ;它不支持单个字符串中的多个逗号分隔表达式。)
为什么需要这个的解释是
- PowerMock 通过在其自己的自定义 classloader 中重新加载准备好的 class(以及测试 class)来运行;
- 当
App
调用javax.imageio.ImageIO
时,它最终会尝试加载和初始化内部 classcom.sun.imageio.plugins.jpeg.JPEGImageReader
,然后它会尝试加载其他一些com.sun.imageio
classes 来自 caller class 加载程序; - class 加载失败,因为 PowerMock 的自定义 class 加载器显然找不到那些 JRE classes(很难说清楚此时发生了什么,因为加载由
JPEGImageReader
class 中的本机方法完成 - 也许它也尝试加载一些本机库)。