Passing parameters to maven plugin while writing unit tests - java

I have a maven mojo plugin with parameters like this:
public class SomeMojoPlugin
extends AbstractMojo
{
#Parameter( property = "templatefile", required = true )
private File templateFile;
And I want to write unit tests for this plugin.
How to pass this property/parameter "templatefile" in test methods?

The Maven documentation has a page about How To Use Maven Plugin Testing Harness.
Basically extending AbstractMojoTestCase, implementing the methods for the lifecycle and providing a sample pom.xml file for the test.
public class MyMojoTest extends AbstractMojoTestCase {
}

Related

Create a Maven Plugin that performs parameterised JUnit Tests

We have a multimodule Maven project and intend on performing tests on this. Because our tests are very homogenous, instead of writing the same test over and over, we wrote a parameterised test, which fetches all the files which stand to test and runs its tests against them. Now we want this as a maven plugin so you could just do something like mvn xquerytestrunner:test
I created a separate project, created a Java File in there and annotated it with
#Mojo( name = "xquerytester")
public class XQueryTestRunner extends AbstractMojo {
#Parameter( property = "xquerytester.querytotest", defaultValue = "" )
private static String queryToTest;
public void execute() throws MojoExecutionException, MojoFailureException {
JUnitCore junit = new JUnitCore();
Result result = junit.run(ParameterizedGenericXQueryTest.class);
}
}
Now my question. Will this run? And does it make sense?
My other option was to just have the test in the src/test/java folder of the main module and run it with mvn -Dtest=TestCircle test but the problem is that we use a plugin by Oracle ( oracle-soa-plugin ) that messes up everything around the project but we have to use it.
Our main pom.xml has <packaging>pom</packaging> which is why running the above test goal doesnt work. it just doesnt build or test anything. If i change it to jar the plugin throws errors during build. and I cannot skip build phase because the plugin just does its stuff anyways.
My goal is just to have a one liner for the console that runs my parameterized tests. It just seems Oracle didnt read the howtos on how to write a maven plugin and now i have to work with it.
Update 1:
We now went with a maven plugin. This can be run independent of the oracle soa plugin and also on a mvn project that has a "pom" packagint type.
The Mojo Class:
#Mojo( name = "xsl")
public class XslTestRunner {
#Parameter( property = "xsl.name", defaultValue = "" )
private static String name;
public void execute() throws MojoExecutionException, MojoFailureException {
JUnitCore junit = new JUnitCore();
Result result = junit.run(ParameterizedGenericXslTest.class);
PrintHelper.printResults(result, TransformationTestType.XQUERY);
}
}
The Maven Plugin Pom:
In general I followed these instructions: Your First Plugin

gwt-test-utils does not find my entry point class

I am trying to get gwt-test-utils to work. I set up the project in the following way:
src/main/java : all the java source code
src/test/java : the test source code
src/test/resources : resource files for the tests
I am building my project with gradle and eclipse. Gradle uses these directories correctly by default and I added all three of them as source directories to Eclipse.
I have successfully built and run the project and was able to execute some plain old JUnit tests as well as a GWTTestCase, so I think I set up the project and its dependencies correctly.
Now I wanted to use gwt-test-utils for some more advanced integration tests. To do so I did the following:
Add the gwt-test-utils and gwt-test-utils-csv to my dependencies
gwtTestUtilsVersion = '0.45'
testCompile group:'com.googlecode.gwt-test-utils', name:'gwt-test-utils', version:gwtTestUtilsVersion
testCompile group:'com.googlecode.gwt-test-utils', name:'gwt-test-utils-csv', version:gwtTestUtilsVersion
Add a gwt-test-utils.properties file to the directory src/test/resources/META-INF with the following content:
path/to/my/module = gwt-module
Added a class that extends GwtCsvTest to a package in the src/test/java directory. It is modeled after the second example in HowToWriteCsvScenario from the gwt-test-utils project wiki, replacing occurrence of their example classes with mine. It looks like this
#CsvDirectory(value = "gwtTests")
public class LoginLogoutTest extends GwtCsvTest
{
#Mock
private MainServiceAsync mainService;
private AppController appController = new AppController();
#CsvMethod
public void initApp()
{
appController.onModuleLoad();
}
#Before
public void setup()
{
GwtFinder.registerNodeFinder("myApp", new NodeObjectFinder()
{
#Override
public Object find(Node node)
{
return csvRunner.getNodeValue(appController, node);
}
});
GwtFinder.registerNodeFinder("loginView", new NodeObjectFinder()
{
#Override
public Object find(Node node)
{
return csvRunner.getNodeValue(appController.getRootPresenter().getCurrentlyActiveSubPresenters().iterator().next().getView(), node);
}
});
addGwtCreateHandler(createRemoteServiceCreateHandler());
}
}
added a csv-file for configuring the test to src/test/resources/gwtTests with the following content
start
initApp
assertExist;/loginView/emailTextBox
I tried executing it via the Eclipse's Run As > JUnit Test and indirectly via gradle build (which executes all the test cases, not just this one). Both lead to the same error:
ERROR GwtTreeLogger Unable to find type 'myPackage.client.AppController'
ERROR GwtTreeLogger Hint: Check that the type name 'myPackage.client.AppController' is really what you meant
ERROR GwtTreeLogger Hint: Check that your classpath includes all required source roots
The AppController class is the entry-point configured in the module I configured in gwt-test-utils.properties, which makes me think that configuration works correctly and the rest of the setup (dependencies and all) work as well.
In an earlier version I used the same file as a subclass of GWTTestCase and created an AppController instance in the same way. That worked, so I'm pretty sure the class path is setup correctly to include it as well. I also tried changing it back to the previous version just now and it still works.
I have no clue why the class is not found. Is there anything gwt-test-utils does differently which means I need to specifically set the class path for it? Otherwise it should just work, since both gradle and eclipse know about all the relevant source folders and dependencies.

How to mock maven plugin environment and or project configuration

I want to write unit tests (junit4) for my maven-plugin. All examples i found use "AbstractMojoTestCase" (junit3 :-(). To get rid of this i got answer here. But the problem is how Mojos get instantiated:
MyMojo myMojo = (MyMojo) lookupMojo( "touch", pom );
That means i need a pom for every test case - the pom is the tests input data. But is there a way to mock (i would use Mockito) the project model some how?
Could lookupMojo(String groupId, String artifactId, String version, String goal, PlexusConfiguration pluginConfiguration) be a good starting point? In this case i would mock "PlexusConfiguration", but what methods?
Some maven-plugin testing doku uses classes like "MavenProjectStub". But i can't get a consistent picture of how a mojo is created and to what intefaces it talks on creation.
A perfect solution would be if i could just
#inject
MyMojo testObject;
and just mock all the stuff it need to get it working (primary i need #Parameters)
Based on my experience writing Maven plugin, there are two levels of testing a plugin: via unit test (using mocks) and via integration tests (using the maven-invoker-plugin).
For the integration tests, the maven archetype for new maven plugins already provide a good example out of the box, just execute the following and have a look at it:
mvn archetype:generate \
-DgroupId=sample.plugin \
-DartifactId=hello-maven-plugin \
-DarchetypeGroupId=org.apache.maven.archetypes \
-DarchetypeArtifactId=maven-archetype-plugin
By default you will get integration tests in a profile to start with. An example maven project will also be available (under src\it\simple-it\pom.xml) which can execute your plugin goals. What I suggest is also to enforce the result of your integration test via additional constraints in that pom.xml. For instance: you can add the Maven Enforcer Plugin rule to check against created files, if that makes sense for your plugin.
To answer more specifically to your question on how to write unit tests for custom maven plugins, this is the approach I'm using:
JUnit + Mockito.
Test case running using #RunWith(MockitoJUnitRunner.class)
Mock Maven specific classes (MavenProject, Log, Build, DependencyNode, etc.) using #Mock annotations
Initiate and link your mock objects in a #Before method (typically setUp() method)
Test your plugin :)
As an example, you might have the following mocked objects as class variable of your unit test:
#Mock
private MavenProject project;
#Mock
private Log log;
#Mock
Build build;
Then, in your #Before method you need to add a big of glue code as following:
Mockito.when(this.project.getBuild()).thenReturn(this.build);
For instance, I use to write some custom Enforcer Plugin rules, hence I need
#Mock
private EnforcerRuleHelper helper;
And in the #Before method:
Mockito.when(this.helper.evaluate("${project}")).thenReturn(this.project);
Mockito.when(this.helper.getLog()).thenReturn(this.log);
Mockito.when(this.project.getBuild()).thenReturn(this.build);
Mockito.when(this.helper.getComponent(DependencyGraphBuilder.class)).thenReturn(this.graphBuilder);
Mockito.when(this.graphBuilder.buildDependencyGraph(this.project, null)).thenReturn(this.node);
As such, it will be easy to use these mock objects into your tests. For instance, a must have first dummy test is to test it against an empty build as following (below testing a custom Enforcer rule):
#Test
public void testEmptyBuild() throws Exception {
try {
this.rule.execute(this.helper);
} catch (EnforcerRuleException e) {
Assert.fail("Rule should not fail");
}
}
If you need to test against dependencies of your build, for instance, you might end up writing utility methods as following:
private static DependencyNode generateNode(String groupId, String artifactId, String version) {
DependencyNode node = Mockito.mock(DependencyNode.class);
Artifact artifact = Mockito.mock(Artifact.class);
Mockito.when(node.getArtifact()).thenReturn(artifact);
// mock artifact
Mockito.when(artifact.getGroupId()).thenReturn(groupId);
Mockito.when(artifact.getArtifactId()).thenReturn(artifactId);
Mockito.when(artifact.getVersion()).thenReturn(version);
return node;
}
In order to easily create dependencies into the dependency graph of your build, as following:
List<DependencyNode> nodes = new ArrayList<DependencyNode>();
nodes.add(generateNode("junit", "junit", "4.12"));
Mockito.when(node.getChildren()).thenReturn(nodes);
NOTE: you can improve the utility method if you need further details (like scope or classifier for a dependency).
If you also need to mock configuration of a plugin, because you need to scan existing plugins and their configuration, for instance, you can do it as following:
List<Plugin> plugins = new ArrayList<Plugin>();
Plugin p = new Plugin(); // no need to mock it
p.setArtifactId("maven-surefire-plugin");
Xpp3Dom conf = new Xpp3Dom("configuration");
Xpp3Dom skip = new Xpp3Dom("skip");
skip.setValue("true");
conf.addChild(skip);
p.setConfiguration(conf);
plugins.add(p);
Mockito.when(this.build.getPlugins()).thenReturn(plugins);
I will obviously not cover all the possible cases, but I am sure you got an understanding about approach and usage. Hope it helps.

Maven: How can I run/include a class in a jar with the failsafe plugin?

I've made my own class with an #RunWith(AllTests.class) on it for integration tests that I want to execute, but I've put it in a reusable jar. I'm trying to tell failsafe to use it but I'm not sure how to because the documentation for includes says:
A list of elements specifying the tests (by pattern) that should be included in testing. When not specified and when the test parameter is not specified, the default includes will be
<includes>
<include>**/IT*.java</include>
<include>**/*IT.java</include>
<include>**/*ITCase.java</include>
</includes>
Each include item may also contain a comma-separated sublist of items, which will be treated as multiple entries.
This parameter is ignored if the TestNG suiteXmlFiles parameter is specified.
But this test runner is in the classpath, not on the filesystem. How can I tell failsafe to use my class runner and only my class runner?
Can't you use the #RunWith annotation provided by jUnit?
import com.example.YourTestRunner;
#RunWith(YourTestRunner.class)
public class SomeIntegrationTest {
#Test
public void simpleTest() {
// given, when, then
}
}
Update
Based on the comment:
My class isn't a test runner, but a class that uses #RunWith
you can use inheritance to solve the problem:
#RunWith(SomeTestRunner.class)
#Ignore("Not a real test class because it does not contain any #Test methods, but needed to keep surefire happy")
public class ParentTest {
// this is the reusable class that is in the jar file
}
public class SomeIntegrationTest extends ParentTest {
#Test
public void simpleTest() {
// given, when, then
}
}

Private class unit tests

How to unit test private (means with package visibility) classed in java?
I have a package, only one class is public in this package, other classes are private. How to cover other classed with unit tests? I would like to include unit tests in resting jar.
Create the unit test class in the same package.
For example, If com.example.MyPrivateClass located in src/main/java/com/example/MyPrivateClass.java
Then the test class will be in same package com.example.MyPrivateClassTestCase and will be located in src/test/java/com/example/MyPrivateClassTestCase.java
There are two ways to do this.
The standard way is to define your test class in the same package of the class to be tested. This should be easily done as modern IDE generates test case in the same package of the class being tested by default.
The non-standard but very useful way is to use reflection. This allows you to define private methods as real "private" rather than "package private". For example, if you have class.
class MyClass {
private Boolean methodToBeTested(String argument) {
........
}
}
You can have your test method like this:
class MyTestClass {
#Test
public void testMethod() {
Method method = MyClass.class.getDeclaredMethod("methodToBeTested", String.class);
method.setAccessible(true);
Boolean result = (Boolean)method.invoke(new MyClass(), "test parameter");
Assert.assertTrue(result);
}
}
As indicated in #Kowser's answer, the test can be in the same package.
In Eclipse, and I assume other IDEs, one can have classes in different projects but in the same package. A project can be declared to depend on another project, making the other project's classes available. That permits a separate unit test project that depends on the production project and follows its package structure, but has its own root directory.
That structure keeps the test code cleanly separated from production code.

Categories

Resources