Search code examples
javagradlejunitmockitojava-module

Recommended project structure for modular Java with Gradle, JUnit and Mockito


I'm in the process of trying to migrate a large code base from Java 8 to modular (JPMS) Java 11, and I'm encountering significant pain and finding consistent advice on project structure and how to use module-info files for actual production projects is hard.

The project in question follows the conventional gradle structure for source and test files:

src/main/java/org/abc/...
src/test/java/org/abc/...

I have a module-info.java file in src/main/java/module-info.java, is this the right location? The quick-start for modular java and this seems to contradict this; however other resources do it the way I did.

When trying to run a unit test that looks like this:

@RunWith(MockitoJUnitRunner.class)
public class ABCTest {
    @Mock
    public SomeClass mock;

    ...
}

I get:

Unable to make field public SomeClass mock accessible: module org.abc does not "exports org.abc" 
to module org.mockito

which indicates that I need to add exports org.abc to org.mockito in my module-info.java file but this seems clunky and verbose having to do this for all sub packages in my test tree which aren't even part of the module, and it seems like the wrong place to do it since the tests shouldn't be exported with the release jar.

The seemingly most up to date guidance I could find suggests adding a module-info.[test|java] file to my test tree. What is the difference between these two files (.java and .test)? I couldn't find any hits on Google explaining this. Also I'm supposedly going to have to copy content from the main/java/module-info.java to the test/java/module-info.test and keep them in sync? This seems tedious.

I also understand that gradle doesn't have native support for modular java yet and one must rely on plugins for this, what are the "defacto" plugins that one should use?

I'm confused by the information available, it seems contradicting, very low level or not applicable when using gradle. Could some one please provide an example project with conventional source layout using mockito, junit, gradle and modular java (version 9+, preferably 11)?


Solution

  • Gradle doesn't have built-in out-of-the-box support for JPMS, as you've found out.

    Gradle's JPMS policy can be summed up by this comment from one of the Gradle Core Devs...

    „...There's no short-term plan to work on jigsaw support, so please don't hold your breath...“

    I've developed a plugin that JPMS-enables Gradle and Eclipse though.

    Here is a simple test project that is implemented with Gradle, JPMS, JavaFX 13, JUnit 4 and Mockito. The mrJar JPMS development-enabling plugin is what ties it all together.

    You could run the :test task or the :run task of that test project from the command line with just raw Gradle. Or follow these steps to use the test project in Eclipse...

    1. Import the project into Eclipse

    2. Execute the :eclipse task in Eclipse's Gradle Tasks view

      • this is what triggers the Eclipse Modulefication feature of mrJar
    3. Open the project's properties dialog

      • go to the Java Build Path properties tab
      • notice the notification in the title bar (Not all modules could be found. Click Apply to synchronize)
      • click the Apply button to synchronize the module path
    4. Execute the :test task

      • or alternatively, do Run As Gradle Test from the Package Explorer context menu
      • or alternatively again, executing the :check task is another option that has the same Eclipse Modulefication effect as :eclipse, :test and Run As Gradle Test

    The recording might help clarify the steps you need to follow to run the unit tests and the JavaFX application in Eclipse.

    Hopefully you will find the attached test project pretty straightforward. But if you get stuck, there are more detailed usage instructions and screen recordings in this Gradle Communnity Forum thread.

    I'm also happy to engage with you in that forum thread and clarify anything that might not be obvious in either the video or any of the steps.


    Also, here's a github repository with a fully JPMS-enabled copy of your project @EmilyL...

    JPMS Modules Made Easy