Reputation: 715
I was experimenting jMock as my mocking framework for my project. I came into a situation where I need to mock both a class and an interface. I used the ClassImposteriser.INSTANCE
to initiate the impostor of the context.
Supposing a class Validator
and an interface Person
to mock. When I was going to mock the Interface Person
, I ran to a problem NoClassFoundDefError
. When I mocked the class Validator
, there was no problem.
I need both that class and interface but I can't solve the problem. Please HELP.
Code Example:
Mockery
private Mockery context = new JUnit4Mockery() {{ setImposteriser(ClassImposteriser.Class) }};
Class :
private Validator validator;
Interface :
private Person person;
Inside Test Method
validator = context.Mock(Validator.class);
----> Working
person = context.Mock(Person.class);
---->NoClassFoundDefError
Upvotes: 6
Views: 12092
Reputation: 6865
Since this question keeps coming up in the searches, and it's a bit outdated, I've decided to contribute with an answer.
As @Ogre Psalm33 and @bm212 have already said in their answer and comment, in order to generate a mock for a concrete class, we need to configure the Mockery
's Imposteriser
with a ClassImposteriser.INSTANCE
for versions of JMock prior to 2.11.0, while with a ByteBuddyClassImposteriser.INSTANCE
from JMock 2.11.0 on.
As of jmock 2.11.0,
ClassImposteriser
has been deprecated in favor of the oddly-namedByteBuddyClassImposteriser
. https://github.com/jmock-developers/jmock-library/blob/master/jmock-legacy/src/main/java/org/jmock/lib/legacy/ClassImposteriser.java
However, the ClassImposteriser.INSTANCE
configuration (JMock < 2.11.0) works only up to JDK 15. While, the solution with ByteBuddyClassImposteriser.INSTANCE
(JMock >= 2.11.0) works for every JDK*, as long as the byte-buddy dependency is included when using JDKs 14 and above according to the comment section of the issue #204 in the official JMock's repository on GitHub.
However, despite what is reported in the issues of the official repository, from my tests emerged that when using ByteBuddyClassImposteriser.INSTANCE
without the byte-buddy dependency with JDKs 12 and 13, an IllegaleStateException
is thrown with the message "No code generation strategy available", making this dependency actually necessary from JDK 12 instead of 14.
*All my JMock tests have been conducted from JDK 8 to 21. At the moment of writing this answer, JDK 21 is the latest one.
Mockery context = new JUnit5Mockery() {
{
setImposteriser(ClassImposteriser.INSTANCE);
}
};
Mockery context = new JUnit5Mockery() {
{
setImposteriser(ByteBuddyClassImposteriser.INSTANCE);
}
};
The following example has been implemented using JDK 21, but can be reproduced with any other combination of JDKs and Mockery
configurations described above.
public interface ConnectionFactory {
InputStream getData() throws Exception;
}
public class WebClient {
public String getContent(ConnectionFactory connectionFactory) {
String workingContent;
StringBuffer content = new StringBuffer();
try (InputStream is = connectionFactory.getData()) {
int count;
while (-1 != (count = is.read())) {
content.append(new String(Character.toChars(count)));
}
workingContent = content.toString();
} catch (Exception e) {
workingContent = null;
}
return workingContent;
}
}
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.jmock.imposters.ByteBuddyClassImposteriser;
import org.jmock.junit5.JUnit5Mockery;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import java.io.IOException;
import java.io.InputStream;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
public class TestWebClientJMock {
@RegisterExtension
Mockery context = new JUnit5Mockery() {
{
setImposteriser(ByteBuddyClassImposteriser.INSTANCE);
}
};
@Test
public void testGetContentOk() throws Exception {
ConnectionFactory mockConnFactory = context.mock(ConnectionFactory.class);
InputStream mockInputStream = context.mock(InputStream.class);
context.checking(new Expectations() {
{
oneOf(mockConnFactory).getData();
will(returnValue(mockInputStream));
atLeast(1).of(mockInputStream).read();
will(onConsecutiveCalls(
returnValue(Integer.valueOf('W')),
returnValue(Integer.valueOf('o')),
returnValue(Integer.valueOf('r')),
returnValue(Integer.valueOf('k')),
returnValue(Integer.valueOf('s')),
returnValue(Integer.valueOf('!')),
returnValue(-1)));
oneOf(mockInputStream).close();
}
});
WebClient client = new WebClient();
String workingContent = client.getContent(mockConnFactory);
assertEquals("Works!", workingContent);
}
@Test
public void testGetContentCannotCloseInputStream() throws Exception {
ConnectionFactory mockConnFactory = context.mock(ConnectionFactory.class);
InputStream mockInputStream = context.mock(InputStream.class);
context.checking(new Expectations() {
{
oneOf(mockConnFactory).getData();
will(returnValue(mockInputStream));
oneOf(mockInputStream).read();
will(returnValue(-1));
oneOf(mockInputStream).close();
will(throwException(new IOException("cannot close")));
}
});
WebClient client = new WebClient();
String workingContent = client.getContent(mockConnFactory);
assertNull(workingContent);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>JMockTestClass</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.9.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.9.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jmock</groupId>
<artifactId>jmock-junit5</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>org.jmock</groupId>
<artifactId>jmock-legacy</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>1.14.10</version>
</dependency>
</dependencies>
</project>
Upvotes: 0
Reputation: 1439
The code as you present it won't compile (it should be ClassImposteriser.INSTANCE). The example code below seems to work fine. Perhaps you could provide some more details?
public class Example {
private Mockery context = new JUnit4Mockery() {
{
setImposteriser(ClassImposteriser.INSTANCE);
}
};
@Test
public void testStuff() {
Validator validator = context.mock(Validator.class);
Person person = context.mock(Person.class);
// do some stuff...
}
public static interface Person {
}
public static class Validator {
}
}
Upvotes: 12