jMock Mocking Classes and Interface
Asked Answered
S

2

6

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

Subdeacon answered 9/6, 2009 at 4:43 Comment(1)
please add code examples to make your question clearer. Perhaps I can help you..Aardwolf
A
12

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 {
    }
}
Atrabilious answered 19/6, 2009 at 20:2 Comment(1)
NOTE: As of jmock 2.11.0, ClassImposteriser has been deprecated in favor of the oddly-named ByteBuddyClassImposteriser. github.com/jmock-developers/jmock-library/blob/master/…Shipment
O
0

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-named ByteBuddyClassImposteriser. 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.


Pre JMock 2.11.0

Supported up to JDK 15

Mockery context = new JUnit5Mockery() {
   {
      setImposteriser(ClassImposteriser.INSTANCE);
   }
};

From JMock 2.11.0

Supported up to JDK 21 with byte-buddy dependency

Supported up to JDK 11 without byte-buddy dependency

Mockery context = new JUnit5Mockery() {
   {
      setImposteriser(ByteBuddyClassImposteriser.INSTANCE);
   }
};

Example mocking the concrete class InputStream

The following example has been implemented using JDK 21, but can be reproduced with any other combination of JDKs and Mockery configurations described above.

ConnectionFactory.java

public interface ConnectionFactory {
    InputStream getData() throws Exception;
}

WebClient.java

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;
    }
}

TestWebClientJMock.java

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);
    }
}

pom.xml

<?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>
Oxidize answered 10/2 at 17:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.