Equivalent of api for test dependency in gradle? - gradle

I'm having multi module gradle project. In one of my modules I'm having api dependency:
api('de.flapdoodle.embed:de.flapdoodle.embed.mongo')
I want to change it to dependency that will be visible in tests, across all modules. There is a testImplementation dependency but there is no testApi.
I cannot have this dependency on production classpath anymore since I want to use real mongo instance instead of embedded one. On the other hand I have tests in different modules that depend on data access - in that case I want to run those test with embedded mongo on test classpath.
How I can make this dependency visible in all modules tests?

The question (appears to me) is sharing the test code across modules in a multi-module project
Short answer - No - there is direct test dependency share across modules.
To share test code between modules internally via build settings
Official gradle route https://docs.gradle.org/current/userguide/java_testing.html#sec:java_test_fixtures
Simple hack
testImplementation files(project(':core-module').sourceSets.test.output.classesDirs)
add the above line either individually where you need or in root with subprojects() with appropriate condition
*there are other possible routes as well *
ex: via configuration
child.testImplementation extends parent.testImplementation (or runtime)

testCompileClassPath includes api dependencies so you are all good here, de.flapdoodle.embed.mongo will be visible in your tests.

Related

Why should we define a scope of a dependency in Maven?

Maven provides a tag for dependencies which can "limit the transitivity of a dependency". I understand, that by defining, for instance, a test scope for a given dependency, this dependency will not be available in other phases (diagram). But I don't get what is the advantage of doing so?
Scopes have three main purposes:
avoid that you use something in your application that you did not want to use (if you declare the implementation as runtime, you cannot accidentally use it in your code).
reduce the amount of transitive dependencies. Especially test dependencies will not become dependencies of the users of your library.
reduce the size of a WAR/EAR: If your container already provides the dependencies, you declares them as provided so that they are not packaged into your application.
https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#dependency-scope
You don't need hoe and gun for digging, you just need hoe only.
You don't need JUnit dependency for running on web-server (scope runtime), you need JUnit when you test only (scope test), you don't need JUnit when you package for production.
Another benefit is avoiding version conflicting, avoid unnecessary dependencies redundancy.

Spring Boot Multi Module and Fat jar with Shared Features

Experts,
I need some expert advice on how to approach the below use case in spring boot.
I need to have a maven multi-module approach to my project.
I need to have a single jar as output of the final build process.
There are to be common modules for controllers, data access and other functionality
Other modules are to be created based on functionality domain for eg a module for Payroll, a module for Admin etc etc.
Each domain functional module will then have their own controllers extending the common controller, exception handler and so on.
Each module will also have its own set of thyme leaf pages.
The reason for following such an approach is we have development in phases and we will be rolling out based on functional modules.
Here are the issues that I can sense using this approach.
Where do I add the spring web dependency? If I add to the parent pom - it gets replicated across the children and there will be port conflict issues as each module loads. the same issue will also be there the moment I add it to two child modules.
How do I build the fat jar which has all the jars from all modules and works as the final deployment?
All the text that I read i can't see anything even close to what I am trying to achieve.
AD1. They will not unless you are trying to setup independent application context in each module. Of course you can do that(it might be complicated but I believe it's achievable), but for me it's an overkill. Personally I think it's better to have one application context and rely on scanning components that are present in classpath.
AD2. The structure in maven might be a little bit complicated and overwhelming at first glance but it makes sense. Here's how I see it:
Create a parent module that will aggregate each module in project and will declare library/plugin dependencies for submodules.
Create 1-N shared submodules that will be used in other modules. With come common logic, utils, etc.
Create 1-N submodules that will be handling your business logic
Create an application submodule that creates application context and loads configuration and components from classpath
Create a submodule that will be responsible for packaging process, either to war, jar, uber-jar or whatever else you desire. Maven jar plugin should do that for you. For executable uber-jar, you have dedicated tool from spring.
Now you can choose three ways(these ways I know) of loading your modules.
1. Include some modules in maven build based on the build configuration via maven profiles and let spring IoC container load all the components he finds in the classpath
2. Include all of the modules in maven build and load them depending on spring active profiles - you can think about it as of feature flag. You annotate your components or configuration class with #Profile("XYZ") telling spring IoC container whether to instantiate component or not. You will need (most flexible solution) to provide a property file which tells spring which profiles are active and thus which modules should be loaded
3. Mix of these two above.
Solution 1 pros:
build is faster (modules that are not included will be skipped during build)
final build file is light (modules that are not included are... not included ;))
nobody can run module that is not present
Solution 1 contras:
project descriptor in maven may explode as you might have many different profiles
Solution 2 pros:
it's fairly easy and fun to maintain modules from code
less mess in project descriptor
Solution 2 contras:
somebody can run module that is not intended to be run as it's present in classpath, but just excluded during runtime via spring active profiles
final build file might be overweight - unused code is still present in code
build might take longer - unused code will be compiled
Summary:
It's not easy to build well structured project from scratch. It's much more easier to create a monolith and then split it into modules. It's because if you already created a project, you've probably already identified all the domains and relations between them.
Over past 8 years of using maven, I honestly and strongly recommend using gradle as it's far more flexible than maven. Maven is really great tool, but when it comes to weird customization it often fails as it's build capabilities rely on plugins. You can't write a piece of code on the fly to perform some custom build behaviour while buidling your project, you must have a dedicated plugin for doing that. If such plugin exists it's fine, if it's not you will probably end up writing your own and handling its shipment, so anyone in your company can easily perform project build.
I hope it helps. Have fun ;)

maven test dependencies of dependencies

I have a large project and i want to add an integration tests module which will depend on every thing and validate interaction between modules.
The issue is that during the test I'm missing the dependency classes
module A uses module B
I have a test on module A testing something that uses module B, and I'm getting an error stating it can't find classes in module B.
I tried surefire but there is no difference.
I know I can and I should mock the classes in B which aren't part of the test but I want a full test that will test everything.
As official Maven documentation declares test scope is not transitive. You need to declare required dependencies explicitly in the pom file(s).
You cannot change this behaviour, but there is usually no need to change this behaviour.
If you want to write a library that is used for testing, then this library should have compile dependencies, but when you use it, declare it with scope test.

Should I create a new spring-boot starter or use optional dependencies?

I'm currently maintaining a fork of the jodconverter project which offers a spring-boot-starter module, allowing a spring-boot based app to use an Open/Libre Office installation (on the same server) to automate document conversions.
Now, the project have grown and a new module was born, named jodconverter-online. This module will send conversion request to a LibreOffice Online server, and I now want to create a spring-boot starter to support this new module.
The current jodconverter-local (on which depends the current jodconverter-spring-boot-starter) does not have the same dependencies as the jodconverter-online module. This is why they are two separated modules in the first place.
So my question is:
Should I create a new jodconverter-online-spring-boot-starter or if it is possible (and how) to just modify the current starter project, making the dependencies optional according to the needs of the user.
For now I put it all in the current starter project (which is available as a 4.2.0-SNAPSHOT in the OSS snapshot repository), but I'm doing it the wrong way since it automatically adds the dependencies for both the jodconverter-local and the jodconverter-online modules.
You may want to make the dependencies to jodconverter-local and jodconverter-online optional, you just need to replace the keyword compile by compileOnly in your Gradle build file.
Obviously, when dependencies become optional, the developer will have to choose one of the options and add it to their project's dependencies (in addition to your starter).
If the only additional dependency is either jodconverter-local or jodconverter-online, that is no big deal. But if more dependencies have to be added for each case, then you might consider creating a new starter to encapsulate those dependencies.
As for the AutoConfigurations, I don't see any problem with what you did, since you use #ConditionalOnClass to trigger the AutoConfiguration only when the corresponding class is present on the classpath.

Understanding Maven scoping better

I have been struggling to figure out what's the use of scoping that is provided by Maven
as mentioned here.
Why should you not always have compile time scoping? Real life examples would be really appreciated.
The compile scoped dependencies are only used during compilation.
The test scoped ones -- only during tests. Say you have tests using junit, or easymock. You obviously do not want your final artifact to have a dependency on them, but would like to be able to just depend on these libraries while running your tests.
Those dependencies which are marked provided are expected to be on your classpath when you're running the produced artifact. For example: you have a webapp and you have a dependency on the servlet library. Obviously, you should not package it inside your WAR file, as the webapp container will already have it and a conflict may occur.
One of the reasons to have different scopes for dependencies is that different parts of the build can depend on different dependencies. For example, if you are only compiling your code and not executing any tests, then there is no point in having Maven downloading your test dependencies (if they're not already present in your local repository, of course). The other reason is that not all dependencies need to be placed in your final artifact (whether it's an assembly, or WAR file), as some of the dependencies are only used during the build and testing phases.
compile
Will copy these jar files into prepared War file.
Ex: hibernate-core.jar need to have in our prepared War.
provided
These jars will be considered only at complie time and test time
Ex:
servlet.jar will be provided by deployed server, so no need to provide from our prepared War file.
test
These jars are only required for running test classes.
Ex: Junit.jar will be required only for running Junit test classes, no need to deploy these.
Scopes are quite well explained in here:
https://maven.apache.org/pom.html#Dependencies
As a reference, I copied the paragraph:
scope: This element refers to the classpath of the task at hand
(compiling and runtime, testing, etc.) as well as how to limit the
transitivity of a dependency. There are five scopes available:
compile
- this is the default scope, used if none is specified. Compile dependencies are available in all classpaths. Furthermore, those
dependencies are propagated to dependent projects.
provided - this is
much like compile, but indicates you expect the JDK or a container to
provide it at runtime. It is only available on the compilation and
test classpath, and is not transitive.
runtime - this scope indicates
that the dependency is not required for compilation, but is for
execution. It is in the runtime and test classpaths, but not the
compile classpath.
test - this scope indicates that the dependency is
not required for normal use of the application, and is only available
for the test compilation and execution phases.
system - this scope is
similar to provided except that you have to provide the JAR which
contains it explicitly. The artifact is always available and is not
looked up in a repository.
there are a couple of reasons that you might not want to have all dependencies to be default compile scope
reduce the size of final artifact(jar,war...) by indicating different scope.
when you have a multiple-modules project, you have ability to let each module have it's own version of dependency
avoid class version collision by provided scope, for instance if you are going deploy a war file to weblogic server, you need to get rid of some javax jars, like javax.servlet, javax.xml.parsers, JPA jars and etc. otherwise you might end up with class collision error.

Resources