ClassNotFoundException in cucumber framework - java

I am using Cucumber framework for mobile app testing. In pom.xml, I have given this below plugin to run TestClass.java - which has code for uploading the latest APK version of the app. Main method is present inside this TestClass. I need this to run before the actual test execution. So I have used exec plugin. I'm getting this error if I am running with pom.xml --> mvn clean test. ClassNotFoundExpection is always thrown with pom.xml, but the individual class runs perfectly.
pom.xml:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<id>installAPK</id>
<phase>generate-test-sources</phase>
<goals>
<goal>java</goal>
</goals>
</execution>
</executions>
<configuration>
<includePluginDependencies>true</includePluginDependencies>
<mainClass>org.com.package1.TestClass</mainClass>
</configuration>
</plugin>
Console error:
java.lang.ClassNotFoundException: org.com.package1.TestClass
at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
at org.codehaus.mojo.exec.ExecJavaMojo$1.run(ExecJavaMojo.java:246)
at java.lang.Thread.run(Thread.java:748)
I also tried changing the phase after test-compile. Still i am getting the same error. Someone pls help.

According to the exec-maven-plugin documentation, the default dependency scope for the execution is runtime. Please change it to test with the following configuration if the TestClass is part of the test sources.
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.0.0</version>
<executions>
...
</executions>
<configuration>
...
<classpathScope>test</classpathScope>
</configuration>
</plugin>

Related

Junit 5 #SpringBootTest executable jar

I am trying to create an executable jar with my test. I cannot use maven to run the tests so I am trying to create a jer that will execute them.
I have a jar with all the test's and with all dependencies. But, when I try to run:
java -jar target/tests-0.0.1-SNAPSHOT-spring-boot.jar
I am getting -
Exception in thread "main" java.lang.ClassNotFoundException: com.xxx.xx.tests.framework.SDAutomation
at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at org.springframework.boot.loader.LaunchedURLClassLoader.loadClass(LaunchedURLClassLoader.java:93)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
pom.xml include the following to create the jar:
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<classifier>spring-boot</classifier>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.6</version>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals> <goal>single</goal> </goals>
<configuration>
<archive>
<manifest>
<mainClass>com.xxx.xx.tests.framework.SDAutomation</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
</plugin>
Any help will be great.
Edit 1:
found a seleution by creating a main with LauncherFactory
for example:
LauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder.request()
.selectors(
selectClass(SDAutomation.class)
)
.build();
Launcher launcher = LauncherFactory.create();
// Register a listener of your choice
SummaryGeneratingListener listener = new SummaryGeneratingListener();
launcher.registerTestExecutionListeners(listener);
launcher.execute(request);
TestExecutionSummary summary = listener.getSummary();
taken from juint doc
Spring boot works with a Jar which is not a Jar really. When you use spring-boot-maven- plugin - you instruct maven to prepare such a spring boot jar. I suggest to open this jar with some kind of WinRar/WinZip application and see what's inside actually.
So, if you have a spring boot jar and try to run it as a spring boot application it will load the main class and run it.
This main class should run programmatically all the automation tests and If I understand correctly this is exactly what SDAutomation is supposed to do.
But then you use maven assembly plugin and try to edit its manifest and specify the main plugin - do not do it. Instead specify the main class in a spring boot plugin. So this is clearly a mistake
The third step is even more confusing - you create a test jar, that's cool, but where do you use it?
So how to really solve this issue:
You should decide whether you want to use spring boot at all to run tests. Frankly I don't see a point of doing so - you could create a fat jar of all the dependencies and run tests from there. Unless these tests are not designed as #SpringBootTest which is also doesn't make sense for automation tests, here is why:
Spring Boot Test is an integration framework as opposed to automation tests which are usually cover the end to end flow. Automation tests run in the different JVM as the application as opposed to spring boot tests that provide a lot of convenience features for loading the application context of the real application inside of the test (or a part of application context). Automation tests should not load any production code at all... Probably if you could elaborate more on the purpose of the automation test suite - I could provide more information.
Even if you run the spring boot application, it should not be the same spring boot application that runs the production code. Probably you should create another maven module, provide a dependency on the artifact with a test classifier and in the main method use console application (again, no web server in this case is required) to run the test suite.
Last but not the least. Surefire/failsafe plugins of maven besides actually running the tests, also produce reports that can be integrated with a CI tool. Since you won't have any maven / surefire there (from your comment) you won't be able to benefit from these reports.
Below are steps ->
Step 1: Add the below plugins to your POM file -
<build>
<finalName>dockerized-springfield</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<release>11</release>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>prepare-package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>
${project.build.directory}/libs
</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.18.1</version>
<configuration>
<suiteXmlFiles>
<suiteXmlFile>src/test/java/com/automation/framework/gui/testng.xml</suiteXmlFile>
</suiteXmlFiles>
<argLine>
-javaagent:"${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar"
</argLine>
<classpathDependencyExcludes>
<classpathDependencyExclude>com.vaadin.external.google:android-json</classpathDependencyExclude>
</classpathDependencyExcludes>
</configuration>
</plugin>
</plugins>
</build>
Step 2: Run mvn commands to create jars
mvn package -DskipTests
This will create 3 jars files inside your target folder
application.jar [Springboot jar which contains code inside "main" folders along with the dependencies.]
application.jar.original [Jar which ONLY contains code inside "main" folders]
application-tests.jar [Jar which contains code inside the "test" folder.]
We only need the 2nd and 3rd jar files. You may delete the first file.
Now rename the second file and remove the ".original" from its file name. Let's say mainapplication.jar
We will also need the libs folder which contains jar files for respective dependencies mentioned in the POM file.
Step 3: Run your jar files.
java -cp mainapplication.jar:application-tests.jar:libs/* org.testng.TestNG testng.xml

Run scala app with maven dependencies plus some java code

I have created a Scala application. This application has some maven dependencies. But this application has also some java code. Scala code is under
src/main/scala/...
and the java code is under
src/main/java/...
which I use through scala. The main class is a scala object (not java). I compile the application successfully via IntelliJ. When I try to run the application using:
scala -J-Xmx4g MyApp.jar
I get the following exception:
java.lang.NullPointerException
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:348)
at scala.reflect.internal.util.ScalaClassLoader$$anonfun$tryClass$1.apply(ScalaClassLoader.scala:43)
at scala.reflect.internal.util.ScalaClassLoader$$anonfun$tryClass$1.apply(ScalaClassLoader.scala:43)
at scala.util.control.Exception$Catch$$anonfun$opt$1.apply(Exception.scala:119)
at scala.util.control.Exception$Catch$$anonfun$opt$1.apply(Exception.scala:119)
at scala.util.control.Exception$Catch.apply(Exception.scala:103)
at scala.util.control.Exception$Catch.opt(Exception.scala:119)
at scala.reflect.internal.util.ScalaClassLoader$class.tryClass(ScalaClassLoader.scala:42)
at scala.reflect.internal.util.ScalaClassLoader$class.tryToInitializeClass(ScalaClassLoader.scala:39)
at scala.reflect.internal.util.ScalaClassLoader$URLClassLoader.tryToInitializeClass(ScalaClassLoader.scala:101)
at scala.reflect.internal.util.ScalaClassLoader$class.run(ScalaClassLoader.scala:63)
at scala.reflect.internal.util.ScalaClassLoader$URLClassLoader.run(ScalaClassLoader.scala:101)
at scala.tools.nsc.CommonRunner$class.run(ObjectRunner.scala:22)
at scala.tools.nsc.JarRunner$.run(MainGenericRunner.scala:13)
at scala.tools.nsc.CommonRunner$class.runAndCatch(ObjectRunner.scala:29)
at scala.tools.nsc.JarRunner$.runJar(MainGenericRunner.scala:25)
at scala.tools.nsc.MainGenericRunner.runTarget$1(MainGenericRunner.scala:69)
at scala.tools.nsc.MainGenericRunner.run$1(MainGenericRunner.scala:87)
at scala.tools.nsc.MainGenericRunner.process(MainGenericRunner.scala:98)
at scala.tools.nsc.MainGenericRunner$.main(MainGenericRunner.scala:103)
at scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala)
Note that I can run the application from the IDE. Also note that I have added some maven plugins as suggested from other websites with similar exceptions. The plugins are:
<plugins>
<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
<executions>
<execution>
<id>scala-compile-first</id>
<phase>process-resources</phase>
<goals>
<goal>add-source</goal>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
What is the reason of the exception?
The answer to my question was quite simple. The folder scala had to be marked as "Sources Root" and the folder java had to be marked as "Generated Sources Root" from IntelliJ.
The application now works as expected.

Run java class from maven if parameter is specified

I'm trying to run a java class which populates my database with dummy data. In eclipse I do it just by right clicking and running as java program. The problem is I'd like make jenkins do it... obvious solution would be running a class using maven as it would put everything needed on the classpath.
I have tried http://mojo.codehaus.org/exec-maven-plugin/ like this:
<profile>
<id>populatedb</id>
<activation>
<activeByDefault>false</activeByDefault>
<property>
<name>populatedb</name>
</property>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.2.1</version>
<executions>
<execution>
<phase>install</phase>
<goals>
<goal>java</goal>
</goals>
</execution>
</executions>
<configuration>
<mainClass>com.example.DatasetReader</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</profile>
But it gives me ClassNotFound on com.example.DatasetReader before the project is even built. I use this command:
mvn clean install exec:java -Dpopulatedb -Dclasspath -Dexec.mainClass="com.example.DatasetReader"
I think it have to do something with execution phase... but there is nothing like post-install...
Thanks!
I think the problem is to do with the classpath that is used by the exec-maven-plugin. By default the exec-maven-plugin uses the runtime classpath. I presume that your DatasetReader class is a test class so is only available on the test classpath.
To pass a different classpath to the exec-maven-plugin you use the classpathScope property.
So you would use <classpathScope>test</classpathScope> in your pom to have the plugin run with the test classpath.
So you would simply need to modify your POM to be as follows:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.2.1</version>
<executions>
<execution>
<phase>install</phase>
<goals>
<goal>java</goal>
</goals>
</execution>
</executions>
<configuration>
<classpathScope>test</classpathScope> <!-- this is the extra config -->
<mainClass>com.example.DatasetReader</mainClass>
</configuration>
</plugin>
Try without additional phases:
mvn exec:java -Dexec.mainClass="com.example.DatasetReader"
or adding classpath scope from runtime:
mvn exec:java -Dexec.mainClass="com.example.DatasetReader" -Dexec.classpathScope=runtime

Code coverage of client/server web application

I am writing a multi-module application. Some of the modules are just basic Java libraries which are then included in the WAR of a webapp.
I would like to run code coverage in the following scenario:
I am running the webapp through an embedded Jetty that is started via Maven.
I have tests which are executing HTTP requests against the webapp.
I would like to get code covered in the webapp and also by the tests.
Is this possible and how can it be achieved with Cobertura, JaCoCo or Emma? From what I understand, the code coverage will only cover the client-side code in this scenario. Am I correct?
I think if you would manage to attach the JaCoCo-agent to the jvm that runs the jetty, it should be able to measure which code has been called over the time you run the integration tests against your webapp. So you should get a statistic that shows you the code coverage.
There is a JaCoCo Maven Plugin - though I'm not sure if this will help with you scenario. Just used it during unit tests.
Edit: found a blog-post that seems to point in the right direction here
Measure Code Coverage by Integration Tests with Sonar
Here's how I achieved it
Assuming you already have a minimal pom.xml config:
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</
<version>0.7.4.201502262128</vers
</plugin>
Download JaCoCo's agent and copy jacocoagent.jar to a suitable location (e.g. $HOME/tools/jacocoagent0.7.4.jar)
Attach JaCoCo's agent to Maven's JVM via:
export MAVEN_OPTS="$MAVEN_OPTS \
-javaagent:$HOME/tools/jacocoagent0.7.4.jar=output=tcpserver,port=6300"
Run your application with embedded jetty server e.g. mvn jetty:run
Run your integration tests
In another shell, dump and report via mvn jacoco:dump jacoco:report
Open your report on ./target/site/index.html (by default)
You can use Jacoco plugin to generate code coverage Here is the plugin configuration I used for junit test code coverage.
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.5.10.201208310627</version>
<configuration>
<skip>${maven.test.skip}</skip>
<output>file</output>
<append>true</append>
</configuration>
<executions>
<execution>
<id>jacoco-initialize</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>jacoco-site</id>
<phase>verify</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
Note: you may get life cycle not covered error in maven while using eclipse, one way is you explicitly mention the life cycle using plugin management. I installed the jacoco plugin from the market place which resolved my problem
We had a similar scenario where integration test were run on a jetty server. Also we needed a combined report for all the tests unit and integration. The solution we implemented was to run-forked jetty and pass the jvmargs with the jacoco javaagent details. Our code coverage reports covered all the rest api's and the service layer java code.
The pom config for jacoco
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>${jacoco-maven-plugin.version}</version>
<configuration>
<append>true</append>
</configuration>
<executions>
<execution>
<id>prepare-test</id>
<goals>
<goal>prepare-agent</goal>
</goals>
<configuration>
<destFile>${project.build.directory}/jacoco.exec</destFile>
<propertyName>surefireArgLine</propertyName>
</configuration>
</execution>
<execution>
<id>prepare-integration</id>
<goals>
<goal>prepare-agent-integration</goal>
</goals>
<configuration>
<destFile>${project.build.directory}/jacoco.exec</destFile>
<propertyName>failsafeArgLine</propertyName>
</configuration>
</execution>
</executions>
</plugin>
With the above config we generated a common exec file for both unit and integration test. Next we configured jetty to run-forked
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>${jetty-maven-plugin.version}</version>
<configuration>
<stopKey>foo</stopKey>
<stopPort>9999</stopPort>
<webApp>
<contextPath>/myway</contextPath>
<descriptor>src/main/webapp/WEB-INF/web.xml</descriptor>
</webApp>
<!-- passing the jacoco plugin as a jvmarg -->
<jvmArgs>${failsafeArgLine}</jvmArgs>
</configuration>
<executions>
<execution>
<id>start-jetty</id>
<phase>pre-integration-test</phase>
<configuration>
<daemon>true</daemon>
<waitForChild>false</waitForChild>
</configuration>
<goals>
<goal>run-forked</goal>
</goals>
</execution>
<execution>
<id>stop-jetty</id>
<phase>post-integration-test</phase>
<goals>
<goal>stop</goal>
</goals>
</execution>
</executions>
</plugin>
This would launch jetty in a separate jvm with the jvmargs. Finally we generated the report in the reporting tag of the pom. We noticed that adding the report to the build plugins did not capture the integration tests run by the jetty.
<reporting>
</plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>${jacoco-maven-plugin.version}</version>
<reportSets>
<reportSet>
<id>jacoco-report</id>
<reports>
<report>report</report>
</reports>
</reportSet>
</reportSets>
</plugin>
</plugins>
</reporting>
The reports can be accessed at target/site/jacoco/index.html, alternately you can run it from the command line.
mvn jacoco:report
Hope it helps.

Executing Maven unit tests with classpath set to generated project JAR

Some unit tests in my application are related to finding and manipulating certain files resources that are part of the application itself.
I need these tests to be executed in the real production setting of the application, where it is deployed as a JAR file, not as an exploded directory.
How could I instruct Maven to execute my unit tests considering as the classpath the project generated jar file (and any other declared library dependencies) instead of the compiled classes in the file system as it does by default?.
In other words, right now the classpath for my unit tests is set to: /$PROJECTPATH/target/classes/.
Instead, I would like this classpath to be set to: /$PROJECTPATH/target/myjarfile.jar.
I have tried manually adding and removing dependency classes, as explained here:
http://maven.apache.org/plugins/maven-surefire-plugin/examples/configuring-classpath.html
,but until now it is not working.
My current project POM looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>org.mygroupid</groupId>
<artifactId>myartifact</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
...
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.1.2</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.4</version>
<configuration>
<includeScope>runtime</includeScope>
<outputDirectory>${project.build.directory}/lib</outputDirectory>
</configuration>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>process-resources</phase>
<!-- <phase>package</phase> -->
<goals>
<goal>copy-dependencies</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.12.3</version>
<configuration>
<classpathDependencyExcludes>
<classpathDependencyExclude>
${project.build.outputDirectory}
</classpathDependencyExclude>
</classpathDependencyExcludes>
<additionalClasspathElements>
<additionalClasspathElement>
${project.build.directory}/${project.build.finalName}.${project.packaging}
</additionalClasspathElement>
</additionalClasspathElements>
</configuration>
</plugin>
</plugins>
</build>
</project>
Thanks in advance for any help!.
The standard unit tests executed as part of the test lifecycle phase cannot see the project JAR because the test phase is executed before the package phase, so your tests are run before Maven generates the JAR. See this page for a list of lifecycle phases and their order.
What you want it to run your tests as integration tests, which execute in the integration-test phase.
There are a number of tutorials for setting up Maven to run integration tests. Here and here are a couple of starters. The failsafe plugin is typically used for executing integration tests.
I can't recall exactly if integration tests use target/classes or your project's JAR file in the classpath. But if it doesn't you could always create another Maven project, add your tests in there and add the main project as a dependency to this integration test project. In some cases this can be preferable to using the integration test phase in the main project if it is not just a standard Java library, for example if you are writing an annotation processor.

Categories

Resources