2

In a project (Eclipse IDE 4.12) with Maven I'm using successfuly JUnit 5 and Mockito 4.0.0 for testing / mockig. Now, to test protected methods, I want to use PowerMock too.

After searching the web for a while I end up with this pom.xml:

<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>sdi</groupId>
<artifactId>id4mqtt</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Id4Mqtt</name>
<description>Rapid IoT development framework</description>

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.target>1.8</maven.compiler.target>
    <maven.compiler.source>1.8</maven.compiler.source>
</properties>

<repositories>
    <repository>
        <id>Eclipse Paho Repo</id>
        <url>%REPOURL%</url>
    </repository>
</repositories>

<dependencies>
    <!-- https://mvnrepository.com/artifact/org.mockito/mockito-inline -->
    <dependency>
        <groupId>org.mockito</groupId>
        <artifactId>mockito-inline</artifactId>
        <version>4.0.0</version>
        <scope>test</scope>
    </dependency>
    
    <!-- https://mvnrepository.com/artifact/org.apache.geronimo.gshell.support/gshell-io -->
    <dependency>
        <groupId>org.apache.geronimo.gshell.support</groupId>
        <artifactId>gshell-io</artifactId>
        <version>1.0-alpha-2</version>
    </dependency>
    
    <!-- https://mvnrepository.com/artifact/org.powermock/powermock-mockito-release-full -->
    <dependency>
        <groupId>org.powermock</groupId>
        <artifactId>powermock-mockito-release-full</artifactId>
        <version>1.6.4</version>
        <type>pom</type>
        <scope>test</scope>
    </dependency>

</dependencies>

The candidate to test is (code not of interest is deleted)

package applicationBuilder;

import java.util.ArrayList;
import java.util.List;
import java.util.Properties;


public class ConfigurationBuilder extends BuilderBase {

    private Properties properties;
    private String[] uriList;
     
    public ConfigurationBuilder(final Properties aProperties) {
        properties = aProperties;
    } // ConfigurationBuilder(...)
    
    public Object build(final String aBrokerName) {

        List<String> brokerList = grepPropertyKey("[^\\.]+\\.CommunicationMode");
        List<MQTTClientDao> clientDaoList = new ArrayList<>();

        for(String broker : brokerList) {
            clientDaoList.add(buildConfig(broker));
        } // rof(broker)
        
        return clientDaoList;
        
    } // build()

    protected MQTTClientDao buildConfig(String broker) {
        
        buildUriList(broker);
        
        return new MQTTClientDao(mqttClient, connectOptions);
        
    } // buildConfig

    protected void buildUriList(final String aBroker) {
        
        ServerUriBuilder builder = new ServerUriBuilder(properties);
        uriList = (String[]) builder.build(aBroker);
        
    } // buildUriList()

} // class

The testclass which should test the correct call of ServerUriBuilder.build(properties) method looks like this:

package test.applicationBuilder;

import static org.powermock.api.mockito.PowerMockito.*;

import java.util.Properties;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.legacy.PowerMockRunner;

import applicationBuilder.ConfigurationBuilder;
import applicationBuilder.ServerUriBuilder;

@RunWith(PowerMockRunner.class)
@PrepareForTest(ServerUriBuilder.class)
class ConfigurationBuilderTest {

    private static Properties properties;
    private static final String BROKER_NAME = "Broker1";
    
    
    @BeforeEach
    void setUp() throws Exception {
        
        properties = new Properties();
        
    }

    @AfterEach
    void tearDown() throws Exception {
        
        properties = null;
        
    }

    @Test
    final void testBuildUriList() {
        
        ConfigurationBuilder cut = spy(new ConfigurationBuilder(properties));
        
        try {
            
            verifyPrivate(cut).invoke("buildUriList", BROKER_NAME);
            
        } catch (Exception e) { e.printStackTrace(); }
        
    } // testBuildUriList()

    
} // class

Every thing seems to be fine because Eclipse reports no errors. But when I run the test a exception is thrown:

java.lang.NoClassDefFoundError: org/mockito/cglib/proxy/MethodInterceptor
    at org.powermock.api.mockito.PowerMockito.spy(PowerMockito.java:220)
    at test.applicationBuilder.ConfigurationBuilderTest.testBuildUriList(ConfigurationBuilderTest.java:130)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:628)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:117)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:184)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:180)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:127)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:68)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:135)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
    at java.util.ArrayList.forEach(ArrayList.java:1257)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
    at java.util.ArrayList.forEach(ArrayList.java:1257)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:229)
    at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$6(DefaultLauncher.java:197)
    at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:211)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:191)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:137)
    at org.eclipse.jdt.internal.junit5.runner.JUnit5TestReference.run(JUnit5TestReference.java:89)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:41)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:541)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:763)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:463)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:209)
Caused by: java.lang.ClassNotFoundException: org.mockito.cglib.proxy.MethodInterceptor
    at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    ... 55 more


My questions are:

  1. Can I use this combination for testing?
  2. Is my pom.xml correct and contains all required modules?
  3. How to make the test run?

Thanks in advance!

Stefan D.
  • 299
  • 3
  • 8
  • The class you are failing on exists in https://mvnrepository.com/artifact/org.mockito/mockito-all/1.9.5 – JCompetence Nov 24 '21 at 12:48
  • 1
    Powermockito is JUnit 4 based as `@RunWith(PowerMockRunner.class)` this shows... that does not work with JUnit Jupiter (aka. Junit 5.)...the other question is: Why do you need powermockito? Why not using mockito instead? The method `buildUril....` is protected but not private?... – khmarbaise Nov 24 '21 at 13:01
  • 1
    When using junit5 it is better to use together with mockito-inline to replace powermock – Felipe Nov 25 '21 at 07:15
  • @khmarbaise: Here https://stackoverflow.com/a/34093433/5564903 is explained how testing protected methods can be done. But none of the three solutions is acceptable for me. Neither like to mix production and testcode in the same package nor to change production code just for testability. Also a wraper class inthe testcode is not an option because the class under test has a lot of private methods to test ... Next is that i have some more builder classes to test, all of them following the same basic patern. With wraper classes each test has the same ugly and error prone wraper. – Stefan D. Nov 25 '21 at 08:50
  • 1
    @Felipe: How would a test look like when using mockito-inline? Can you give me an example please? – Stefan D. Nov 25 '21 at 08:51
  • The real issue is to mix JUnit 4 required setup (@RunWith) and JUnit Jupiter which does not has @RunWith..See docs of Junit Jupiter ...Apart from that the release which you are using is 6 years old and might produce issues on newer JDK versions. Furthermore I meant that the method is not private so you can use Mockito to test instead the need for powermock... – khmarbaise Nov 25 '21 at 09:51

1 Answers1

2

I guess you are mixing Junit versions and Powermock is compatible only with Junit 4. Although you can use JUnit 4 with Junit 5, because you are using Powermock you are maybe running on this issue. For example, I saw people using

    <dependency>
        <groupId>org.powermock</groupId>
        <artifactId>powermock-module-junit4</artifactId>
        <version>2.0.2</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.powermock</groupId>
        <artifactId>powermock-module-testng</artifactId>
        <version>2.0.2</version>
        <scope>test</scope>
    </dependency>

But I advise checking the dependencies using mvn dependency:tree and not mix JUnit 5 with Powermock.


Using JUnit 5 with mockito

Add the Mockito-inline dependency that does the same job as Powermockito if you want to mock static methods Using JUnit5.

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-inline</artifactId>
    <version>4.1.0</version>
    <scope>test</scope>
</dependency>

For static methods that do not have arguments, you can use the :: lambda of Java 8. I like to use the try/catch in one line because you don't have to call utilities.close(); after finish your test.

@Test
void givenStaticMethodWithNoArgs_whenMocked_thenReturnsMockSuccessfully() {
    assertThat(StaticUtils.name()).isEqualTo("string-to-mock");

    try (MockedStatic<StaticUtils> utilities = Mockito.mockStatic(StaticUtils.class)) {
        utilities.when(StaticUtils::name).thenReturn("value-name");
        assertThat(StaticUtils.name()).isEqualTo("value-name");
    }
    assertThat(StaticUtils.name()).isEqualTo("string-to-mock");
}

Or for methods that need arguments

@Test
void givenStaticMethodWithArgs_whenMocked_thenReturnsMockSuccessfully() {
    assertThat(StaticUtils.range(2, 6)).containsExactly(2, 3, 4, 5);

    try (MockedStatic<StaticUtils> utilities = Mockito.mockStatic(StaticUtils.class)) {
        utilities.when(() -> StaticUtils.range(2, 6))
          .thenReturn(Arrays.asList(10, 11, 12));

        assertThat(StaticUtils.range(2, 6)).containsExactly(10, 11, 12);
    }

    assertThat(StaticUtils.range(2, 6)).containsExactly(2, 3, 4, 5);
}

the running example can be found here.

Felipe
  • 7,013
  • 8
  • 44
  • 102
  • Is the mockStatic really needed because the method buildUriList is protected but not private nor static? – khmarbaise Nov 25 '21 at 09:56
  • @Felipe: Thanx for your sugestion but I'm sory to say that it doesn't work. The method buildUriList() is not public and so Eclipse croakes 'The method buildUriList(String) from the type ConfigurationBuilder is not visible'. And it's not a static method. – Stefan D. Nov 27 '21 at 09:52