Shared test code in multi-project Maven Java environment - java

I have a Maven multi-project set-up with a parent POM, one library-type jar and several application-jars using this library. Now I'd like to take advantage of this library-type jar's testing facility (especially the API mocking features) in the unit tests of the applications to avoid repeating the same unit test code in all of the applications, which I am doing now. However it seems this is not trivial.
Any of you done this, or any other solutions to suggest to this problem?
[edit]
clarification:
If I have applications A, B and library L, and dependencies like this:
A -> L <- B
I would like the test code of above have dependencies correspondingly:
A' -> L' <- B'
So the application's test code could see the library's test code.

Related

Separate ServiceProviders for Unit Testing and Production Code

I am using ServiceLoader to dynamically load Plugins.
In purpose of unit testing I want to Mock some behavior of these Plugins.
These Mock-Plugins should be used when unit testing, but not when building the Project.
How would one do this though?
I feel like this question is IDE / build tool specific, for that matter I am using Eclipse.
Project Structure:
src
app
app.java
scraper
ScraperController.java
Scraper.java
Scrapers
tests
mockups
FailingScraperMock.jar
...
unit_tests
ServiceLoaderWithProvidedMockScrapersTest.java
...
scraper.Scraper.java is the provider interface
Scrapers is where all the Scraper-Providers go
I tried to exclude the Mock-Plugins for the source folder: src/ .
Either I am doing it wrong or that's not intended for what I am trying to do.
Would appreciate any kind of help / information.
If you mark your test folder (/tests) as test code and then it should be excluded from application builds.

Java, IntelliJ, maven project - importing issues

This might be a silly question, but I need to know. I'll delete it if it's too silly to answer.
In a maven project in IntelliJ, I have the following structure:
procedure
e2e
cucumber (same level as common)
src
test
java
e2e
support
File: ScenarioState.java
package e2e.support
common (same level as cucumber)
src
main
java
common
testdata
File: Case.java
package common.testdata
Is there any way to import the package e2e.support (where ScenarioState.java resides) into the file Case.java in the common.testdata package?
I've been playing around with maven imports, dependencies etc., but I haven't found a way to do it. I might have to redesign some classes to get around it, but that would impact other parts of the project and I'd like to avoid it if possible.
If you really want to do that (and I would strongly recommend to either leave the project alone or restructure it first), define an additional source directory as in
How to add an extra source directory for maven to compile and include in the build jar?
But beware that a project like this will haunt you till the end of time.
One could have in the common's pom.xml a dependency to cucumber with <type>test-jar</type>.
However this violates the concept of src/main for the final product, and src/test for the unit-tests (not incorporated in the product, separate test classes).
(In src/test there can be other classes, so maybe easiest would be for common to have a src/test instead.)
If ScenarioState has nothing to do in src/main, one could place it in a more low-level library cucumberbase in src/main. And make a dependency in cucumber to cucumberbase with <scope>test</scope>. In <common> a normal dependency to cucumberbase.
Keep this main-test separation as otherwise other developers risk insanity.

How can I test a service provider implementation module with Junit 5?

This is my base module which needs implementations of interfaces defined in myspi package. Various providers can offer MyProvider implementations. Base module uses them via myspi.MyProvider interface implementation.
module base {
exports myspi;
uses myspi.MyProvider;
}
This is my sample implementation module which provides the MyProvider implementation with MyProviderImpl
module myspi.provider {
provides myspi.MyProvider with myspi.provider.MyProviderImpl;
}
All these work fine when I load the implementations in base module, with
public static List<MyProvider> getMyProviders() {
var myProviders = new ArrayList<MyProvider>();
for (MyProvider myProvider : ServiceLoader.<MyProvider>load(MyProvider.class)) {
myProviders.add(myProvider);
}
return myProviders;
}
But same code returns empty list in Junit 5 test code (ServiceLoader returns null). How can I test the service provider modules with Junit 5. Or is there any alternative to Junit that allows us to create test modules (modularized test API) that declares "uses myspi.MyProvider" in the module-info and works fine with getMyProviders()?
Basically you're on the right track. You need to convince the Java module system that your test modules are the single source of thruth when it comes to resolve modules are test runtime.
Black-box testing is easy.
White-box testing in the modular world, meaning testing protected and package private members within a module, is tricky. There are at least two ways to achieve this: a) use java command line options to configure the Java module system at test startup or b) blend main sources into the test sources at compile time and maintain a dedicated module-info.java in your test sources.
Please visit the links to the blogs and examples posted over at How to make a modular build with jdk > 1.8
Here is an excerpt for convenience:
Examples
Work-in-progress blueprint https://github.com/sormuras/sandbox/tree/master/sors-modular-testing-blueprint
Integration tests starting with "modular-world-" at https://github.com/sormuras/junit-platform-maven-plugin/tree/master/src/it
Background and other resources
https://github.com/junit-team/junit5-samples/tree/master/junit5-modular-world
https://github.com/forax/pro
https://blog.codefx.org/java/five-command-line-options-to-hack-the-java-9-module-system/
And expect most IDE to not support you either. For now.
SOLVED!
I've removed the Junit from class-path to module-path and also removed all Junit 4 compatibility stuff such as RunWith() etc, and made my test pure Junit 5 test.
I've added a module-info.java (Junit 5 doesn't require an open module although the books tell the opposite)
After I've modularized the tests I found that it still doesn't execute the ServiceLoader stuff. Then I've started looking for the fault myself.
And I found it! Running the ServiceLoader stuff in base module was possible, because the base module refers to the exported myProvider.jar, which in turns access a myProvider-config.properties file in the same directory. Without this config file myProvider cannot work properly.
The problematic test module on the other hand, refered the eclipse project of the myProvider instead of its exported .jar file and hence could not find its config file and exits. I'd moved this config file from Netbeans to Eclipse simply copying it into the same directory. Thus missing config file was the problem.
Changing the project settings I could run the tests without any failure.
I would like to thank all the contributors who responded.
This is quite an old post but if anyone gets here trying to test java modules with junit 5 with gradle, especially the consumer/provider as presented in this post , Sormuras solution is the easy way, to patch the consumer module with the tests classes.
it is supported by gradle-modules-plugin that does that out of the box:
https://github.com/java9-modularity/gradle-modules-plugin

Gradle and JaCoCo: instrument classes from a separate subproject

I have a legacy application that has a unit test module that's separate from the application modules. I'm converting the project to use Gradle and the structure looks like this:
/root
/module1
/module2
...
/moduleN
/test
where the test module executes tests for module1 through moduleN (and depends on them). I know this isn't a very good practice as it kinda defeats the purpose of unit tests but as all know, legacy code is always a headache to work with.
So before I start refactoring the code so that each module has its own unit tests (which means disassembling the test module in a sensible way, i.e., a lot of work) I wanted to find a temporary solution to get the correct code coverage, i.e., have JaCoCo instrument all the classes from module1, ..., moduleN instead of just module test.
Is there a way to tell JaCoCo to instrument classes from other modules?
To include coverage results from subprojects "module*" in the "test" project, you might want to try something like this in your build.gradle from the test project:
// [EDIT] - 'afterEvaluate' NOK, use 'gradle.projectsEvaluated' instead (see comments)
// afterEvaluate {
gradle.projectsEvaluated {
// include src from all dependent projects (compile dependency) in JaCoCo test report
jacocoTestReport {
// get all projects we have a (compile) dependency on
def projs = configurations.compile.getAllDependencies().withType(ProjectDependency).collect{it.getDependencyProject()}
projs.each {
additionalSourceDirs files(it.sourceSets.main.java.srcDirs)
additionalClassDirs files(it.sourceSets.main.output)
}
}
}

Run JUnit tests from a dependency jar in Eclipse

I have some JUnit tests that contained in a .jar that is intended to be used as a library. The library contains some tests that should be run whenever the library is used in another project.
However when I create a new project using the library and run JUnit on it in Eclipse then the tests in the dependency .jar don't run / don't get detected by the JUnit test runner. I get the message:
No tests found with test runner 'JUnit 4'.
Is there a way I can configure the dependency .jar so that the tests will run alongside any tests that might be contained in the main project?
Basically I want the dependency .jar to "export" the tests to whatever projects it is used in.
I'm using Eclipse Juno, JUnit 4.10, and Maven for the dependency management.
EDIT:
The point of this library is to be able to help test projects that use it - i.e. it runs some specialised tests. This is why I want to be able to import the library .jar and have it contribute the extra tests to the importing project.
You can try Maven Surefire.
In some cases it would be useful to have a set of tests that run with various dependency configurations. One way to accomplish this would be to have a single project that contains the unit tests and generates a test jar. Several test configuration projects could then consume the unit tests and run them with different dependency sets. The problem is that there is no easy way to run tests in a dependency jar. The Surefire plugin should have a configuration to allow me to run all or a set of unit tests contained in a dependency jar.
This can be done as follows (Junit 3):
Ensure test jar contains a class which has a static suite() method
import junit.framework.Test;
import junit.framework.TestSuite;
public class AllTests {
public static Test suite()
{
TestSuite suite = new TestSuite( "All Tests");
suite.addTestSuite(TestOne.class);
suite.addTestSuite(TestTwo.class);
return suite;
}
}
Then in the project using the test-jar dependency:
create a TestCase:
package org.melati.example.contacts;
import org.melati.poem.AllExportedTests;
import junit.framework.Test;
import junit.framework.TestCase;
public class PoemTest extends TestCase {
public static Test suite()
{
return AllExportedTests.suite();
}
}
Now the tests will be found.
I think that making a library of unit tests (#Test annotated methods) is a bad idea. However, making a library of reusable test components is a good one. We've done this in a few open source projects, and you can take a look how it works.
One Maven module exports test components (we call them "mocks"), from src/mock/java directory. Exported artifact has -mock classifier. See rexsl/pom.xml (pay attention to highlighted lines).
Mock artifacts are being deployed to Maven Central, together with usual artifacts: http://repo1.maven.org/maven2/com/rexsl/rexsl-core/0.3.8/ (pay attention to ...-mock.jar files)
Modules that need that mocks can include them as usual artifacts, for example rexsl-core/pom.xml (see highlighted lines):
Then, in your unit tests just use the classes from that mock libraries, like regular builders of mocks, for example: BulkHttpFeederTest
That's how you can make your test artifacts reusable, in an elegant way. Hope it helps.
#Mikera,
I find that this may help you. Just extend the Testcase Class to one of your java classes in project and you can run that particular class to run it as a JUnit Test.
I am not sure that this is desirable - On the one hand, if you use a jar, its behaviour might be influenced by the external context, e.g. other libraries in the classpath. From inside the jar, there is no simple way to analyse this context and to adjust the tests accordingly. On the other hand, if you write and compile a library, you should test it before packaging it as a jar. You might even want to not include your tests.
If it is really important to you to run the tests again, I would be interested in what could make them fail without changing the jar. In that case, however, you might want to extend the testrunner. As far as I know it uses reflection. You can quite easily load jars in a classloader and go through all their classes. By reflection you can identify the test classes and assemble testsuites. You could look into the testrunner for an example. Still, you would need to start this process from outside, e.g. from inside one of your test classes in the client project. Here, QATest's approach might be helpful: By providing an overriden version of testsuite or testrunner, you could automate this - if the client uses your overridden API.
Let me know if this rather costly approach seems to be applicable in your scenario and I can provide code examples.
Why should the user of the jar run the test cases inside the jar!!! When the jar is packaged and delivered, it means that the unit tests are run successfully.
Typically, the jar itself should be either treated as a separate project or as one of the modules. In both the cases, unit test cases are run before its delivered.

Categories