Problems with Eclipse executing JUnit tests compiled by gradle


(Daniel Rusev) #1

Here’s my build.gradle file (I’ve rearranged the default project file structure provided by the java plugin):

apply plugin: 'war'
apply plugin: 'eclipse'
  group = 'someGroup'
version = '0.0.1-SNAPSHOT'
  description = "glassfish-hello1"
  /*
 * JVM version
 */
sourceCompatibility = 1.7
targetCompatibility = 1.7
      repositories {
   maven { url "http://maven.repository.redhat.com/earlyaccess/all/" }
 maven { url "http://maven.repository.redhat.com/techpreview/all" }
 maven { url "http://repo.maven.apache.org/maven2" }
}
  /*
 * Override the default java plugin project structure
 * so that it conforms to the JBoss file structure
 */
buildDir = 'target'
  sourceSets {
   // the compiled java classes of this set will be put in target/classes
 main {
  output.classesDir = 'target/classes'
  output.resourcesDir = 'target/classes'
 }
   test {
  output.classesDir = 'target/test-classes'
  output.resourcesDir = 'target/test-classes'
 }
}
  dependencies {
 testCompile group: 'junit', name: 'junit', version:'4.11'
 testCompile group: 'org.jboss.arquillian.junit', name: 'arquillian-junit-container', version:'1.1.0.Final'
 testCompile group: 'org.jboss.arquillian.protocol', name: 'arquillian-protocol-servlet', version:'1.1.0.Final'
   //providedCompile configuration comes from the war plugin, such dependencies will not be included in the war file
 providedCompile(group: 'javax.enterprise', name: 'cdi-api', version:'1.0-SP4') {
  exclude(module: 'jboss-interceptor-api')
  exclude(module: 'jsr250-api')
 }
   providedCompile group: 'org.jboss.spec.javax.annotation', name: 'jboss-annotations-api_1.1_spec', version:'1.0.1.Final'
 providedCompile group: 'org.jboss.spec.javax.ws.rs', name: 'jboss-jaxrs-api_1.1_spec', version:'1.0.1.Final'
 providedCompile group: 'org.hibernate.javax.persistence', name: 'hibernate-jpa-2.0-api', version:'1.0.1.Final'
 providedCompile group: 'org.jboss.spec.javax.ejb', name: 'jboss-ejb-api_3.1_spec', version:'1.0.2.Final'
 providedCompile(group: 'org.hibernate', name: 'hibernate-validator', version:'4.2.0.Final') {
  exclude(module: 'slf4j-api')
 }
   providedCompile group: 'org.jboss.spec.javax.faces', name: 'jboss-jsf-api_2.1_spec', version:'2.0.9.Final'
 providedCompile group: 'org.hibernate', name: 'hibernate-jpamodelgen', version:'1.2.0.Final'
 providedCompile group: 'org.hibernate', name: 'hibernate-validator-annotation-processor', version:'4.2.0.Final'
}
  /*
 * Override the default java plugin project structure
 * so that it conforms to the JBoss file structure
 */
eclipse {
 classpath {
  defaultOutputDir file('target/classes')
    containers = ['org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6']
 }
   project {
  buildCommand 'org.eclipse.wst.jsdt.core.javascriptValidator'
  buildCommand 'org.eclipse.wst.common.project.facet.core.builder'
  buildCommand 'org.eclipse.m2e.core.maven2Builder'
  buildCommand 'org.jboss.tools.jst.web.kb.kbbuilder'
  buildCommand 'org.hibernate.eclipse.console.hibernateBuilder'
  buildCommand 'org.jboss.tools.cdi.core.cdibuilder'
  buildCommand 'org.jboss.tools.ws.jaxrs.metamodelBuilder'
  buildCommand 'org.eclipse.wst.validation.validationbuilder'
    natures 'org.springsource.ide.eclipse.gradle.core.nature',
    'org.eclipse.jem.workbench.JavaEMFNature',
    'org.eclipse.wst.common.modulecore.ModuleCoreNature',
    'org.eclipse.wst.common.project.facet.core.nature',
    'org.hibernate.eclipse.console.hibernateNature',
    'org.jboss.tools.jst.web.kb.kbnature',
    'org.jboss.tools.cdi.core.cdinature',
    'org.jboss.tools.ws.jaxrs.nature',
    'org.jboss.tools.jsf.jsfnature',
    'org.eclipse.wst.jsdt.core.jsNature'
 }
}

If I haven’t executed a gradle task that compiles the test classes and puts them in “target\test-classes” (such as compileTestJava), when, in Eclipse, I click on the project with the right mouse button then “Run As” -> “JUnit Test”, a get a Class not Found Exception. If the .class files are available, then there are no problems.

So each time I create a new test class or make changes to an existing one, I have to run a gradle task before I can run the tests from Eclipse. Isn’t there a way to tell Eclipse to recompile the files automatically?

I’m using the STS plugin for Eclipse, and my Gradle version is 1.9.


(Radim Kubacki) #2

Is org.eclipse.jdt.core.javabuilder enabled for your project? I see a lot of builders there and have no idea what some of them are for. For example I do not know why you need maven2Builder there. OTOH I’d need to play with some example where this can be reproduced to be able to answer your question.


(Daniel Rusev) #3

I think I’ve found the problem. Here’s the gradle generate eclipse .classpath file (after enable dependency management):

<?xml version="1.0" encoding="UTF-8"?>
<classpath>
 <classpathentry kind="src" path="src/main/java"/>
 <classpathentry kind="src" path="src/main/resources"/>
 <classpathentry kind="src" path="src/test/java"/>
 <classpathentry kind="src" path="src/test/resources"/>
 <classpathentry exported="true" kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
 <classpathentry exported="true" kind="con" path="org.springsource.ide.eclipse.gradle.classpathcontainer">
  <attributes>
   <attribute name="org.eclipse.jst.component.dependency" value="/WEB-INF/lib"/>
  </attributes>
 </classpathentry>
 <classpathentry kind="output" path="target/classes"/>
</classpath>

The problem is resolved, if I manually add the following changes to it:

<classpathentry kind="src" output="target/classes" path="src/main/java"/>
 <classpathentry kind="src" output="target/classes" path="src/main/resources"/>
 <classpathentry kind="src" output="target/test-classes" path="src/test/java"/>
 <classpathentry kind="src" output="target/test-classes" path="src/test/resources"/>

So, for some reason when generating the .classpath file, gradle doesn’t take into account what’s written in the build.gradle file. For example, if I change the main sourceSet sourfiles in build.gradle to some other folder:

sourceSets {
  main {
  java.srcDir 'source/java_folder'
        }
}

the generated .classpath file still looks the same:

<classpathentry kind="src" path="src/main/java"/>

when it should be:

<classpathentry kind="src" path="source/java_folder"/>

(Radim Kubacki) #4

Interesting. What version of Gradle do you use?


(Daniel Rusev) #5

Gradle 1.9

Actually, the source folder was not included in the .classpath file because java.srcDir adds an additional source folder, so there are actually two source folders: src/main/java and source/java_folder

If I change the build.gradle file thus:

java.srcDirs = ['source/java_folder']

then it is included the .classpath file. But why aren’t the output folders in .classpath file? How can I get this result:

<classpathentry kind="src" output="target/classes" path="src/main/java"/>

Do I have to use the hooks of the eclipse plug-in (beforeMerged, afterMerged)? How can I have more the one source folder included in the .classpath file?


(Radim Kubacki) #6

Oops, I did not read your original message carefully. Gradle does not set Eclipse output folders to the same directories as its own output. It was observed that doing so makes these tools interfere. Eclipse uses a different compiler and aggressively caches many files. OTOH Gradle wants to make various assumptions about the content in buildDir to optimize builds.

It means if you really want to do it you would really need to do this in one the hooks.


(Daniel Rusev) #7

Yes, I found out that eclipse puts the compiled test classes in ‘target/classes’ and I don’t want them there.


(Daniel Rusev) #8

I’m not very good at Groovy, so this is the solution I came up with:

file {
   whenMerged { classpath ->
    def sourceEntries = classpath.entries.findAll { entry ->
     entry.kind == 'src'
    }
    sourceEntries.each { entry ->
     def sourceSet = sourceSets.find {
      it.java.srcDirs.contains(entry.dir) ||
      it.resources.srcDirs.contains(entry.dir)
     }
     entry.output = sourceSet.output.classesDir.getCanonicalPath().substring(projectDir.getCanonicalPath().length() + 1)
    }
   }
  }

If anyone, who’s more versed in Groovy, knows how the code above could be improved, I’d appreciate the help


(Peter Niederwieser) #9

Making Eclipse and Gradle compile to the same target directory will make the builds less reliable and won’t save any compile time (at least on the Gradle side it may even cost time). (For details see previous forum discussions on this topic.)