Reputation: 9816
After adding powermock (1.5.6 in combination with Easymock 3.2) to my current project (jdk 1.6.0) I get some test failures in test methods which worked perfectly fine before:
java.lang.UnsatisfiedLinkError: com.sun.imageio.plugins.jpeg.JPEGImageReader.initReaderIDs(Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/Class;)V
The following code fails:
BufferedImage img = null;
try {
img = ImageIO.read(this.getClass().getResourceAsStream("/example.jpg"));
}
catch (IOException e) {
fail(e.getMessage());
}
The powermock page already has a bug from 2009 but no fix and no workaround. (Going back to 32Bit is nonsense since those methods work without powermock) So does anybody know how to fix this?
Update I: A switch to 32 Bit is no option and besides that this is not the problem. If I don't use PowerMock every test is working perfectly in my 64Bit JVM...
Update II: Ok here are the requested infos
Update III: Extended the class
Class to be tested
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);
}
}
Test case:
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:
<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>
So if you comment the line:
//@RunWith(PowerMockRunner.class)
it is working. If uncommented it the above error is thrown (again!)
Upvotes: 2
Views: 2516
Reputation: 16390
The solution is to tell PowerMock to ignore all JRE classes which conflict with its custom class loader. That is, add the following annotation to the test class:
@PowerMockIgnore({"javax.imageio.*", "javax.security.*"})
(Note the value
attribute of the annotation takes an array of regular expressions; it does not support multiple comma-separated expressions in a single string.)
The explanation for why this is needed is that
App
calls into javax.imageio.ImageIO
, it ends up trying to load & initialize the internal class com.sun.imageio.plugins.jpeg.JPEGImageReader
, which then attempts to load some other com.sun.imageio
classes from the caller class loader; and JPEGImageReader
class - maybe it attempts to load some native library as well).Upvotes: 10