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

  1. 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);
     }   
    }
    
  2. 测试用例:

    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();
     }
    }
    
  3. 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 属性采用正则表达式的 数组 ;它不支持单个字符串中的多个逗号分隔表达式。)

为什么需要这个的解释是

  1. PowerMock 通过在其自己的自定义 classloader 中重新加载准备好的 class(以及测试 class)来运行;
  2. App 调用 javax.imageio.ImageIO 时,它最终会尝试加载和初始化内部 class com.sun.imageio.plugins.jpeg.JPEGImageReader,然后它会尝试加载其他一些 com.sun.imageio classes 来自 caller class 加载程序;
  3. class 加载失败,因为 PowerMock 的自定义 class 加载器显然找不到那些 JRE classes(很难说清楚此时发生了什么,因为加载由 JPEGImageReader class 中的本机方法完成 - 也许它也尝试加载一些本机库)。