6

Today I tried to switch an project with integration tests from maven to gradle. All worked fine except I have a serious problem with testng.

The project uses hibernate/JPA2 for database access and has a couple of tests that depend on a persistence unit in the test/resources/META-INF/persistence.xml. When I run the test suite using gradle all works fine. But when I run the xml (or any test class by itself) from eclipse it seems that it tries to use the main/resources/META-INF/persistence.xml.

Because I do most of my work using TDD, I realy need to run/debug tests from eclipse. When I add the persistence unit to the production persistence.xml it works (it even gets some other resources from the "test" dir). It would be a workaround, but I realy don't like the idea of adding test resources to "main/resources"

Same project works fine when I import it using the old pom.xml from maven.

build.gradle

apply plugin: 'java'
apply plugin: 'eclipse'

sourceCompatibility = 1.7
version = '0.1'
jar {
    manifest {
        attributes 'Implementation-Title': 'md', 'Implementation-Version': version
    }
}

repositories {
    mavenCentral()
}

dependencies {
    compile 'org.slf4j:slf4j-api:1.7.5'
    compile 'com.google.guava:guava:14.0.1'
    compile 'org.hibernate:hibernate-entitymanager:4.2.2.Final'
    compile 'org.hibernate.javax.persistence:hibernate-jpa-2.0-api:1.0.1.Final'

    testCompile 'ch.qos.logback:logback-classic:1.0.13'
    testCompile 'org.testng:testng:6.8.5'
    testCompile 'org.dbunit:dbunit:2.4.9'
    testCompile 'org.mockito:mockito-all:1.9.5'
    testCompile 'org.easytesting:fest-assert-core:2.0M10'
    testCompile 'org.hsqldb:hsqldb:2.2.9'

}

test {
    useTestNG(){    
        suites 'src/test/resources/testng.xml' 
    } 
}

Update:

Generated classpath file looks like this:

<?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"/>
    <classpathentry exported="true" kind="con" path="org.springsource.ide.eclipse.gradle.classpathcontainer"/>
    <classpathentry kind="output" path="bin"/>
</classpath>

It seems it merges all into the bin folder and doesn't differentiate between main and test like the .classpath file generated by maven.

ssindelar
  • 2,833
  • 1
  • 17
  • 36
  • 1
    Sounds like a class path ordering issue. Does `main/resources` come before `test/resources` in the generated `.classpath` file? If yes, what happens if you change the order? – Peter Niederwieser Jul 03 '13 at 18:44
  • After switching main and test it works. It seems that, unlike maven, gradle generated classpath files merge test and main into the bin folder. – ssindelar Jul 04 '13 at 06:34
  • Thanks Peter. You brought me on the right track to fixing the problem. – ssindelar Jul 04 '13 at 07:39

2 Answers2

13

Problem was that gradle's eclipse plugin merges the test and main folder by default. Therefore the persistence.xml from main did override the test version.

Adding the following code will resolve the problem by changing the output directories of the generated classpathentries and removing the entry with the default output.

import org.gradle.plugins.ide.eclipse.model.SourceFolder 
eclipse.classpath.file {
    beforeMerged { classpath -> 
        classpath.entries.clear()
    }
    whenMerged {  cp -> 
        cp.entries.findAll { it instanceof SourceFolder && it.path.startsWith("src/main/") }*.output = "bin/main" 
        cp.entries.findAll { it instanceof SourceFolder && it.path.startsWith("src/test/") }*.output = "bin/test" 
        cp.entries.removeAll { it.kind == "output" }
    }
}

Update: Relevant classpath entries after the change.

<classpathentry output="bin/main" kind="src" path="src/main/java"/>
<classpathentry output="bin/main" kind="src" path="src/main/resources"/>
<classpathentry output="bin/test" kind="src" path="src/test/java"/>
<classpathentry output="bin/test" kind="src" path="src/test/resources"/>
ssindelar
  • 2,833
  • 1
  • 17
  • 36
  • 1
    I don't quite see how this (reliably) solves the problem. Who makes sure that `bin/test` comes before `bin/main` on the Eclipse test class path? – Peter Niederwieser Jul 04 '13 at 17:54
  • The test code and resourced are compiled to a different directoy than the code/resources of main. Therefore, they no longer override each other. I added the relevant classpath entries to the answer. – ssindelar Jul 05 '13 at 06:20
  • OK. I thought you want to replace the main resource with the test resource. – Peter Niederwieser Jul 05 '13 at 07:15
  • +1, just a note for others who might run into the same problem: I had to delete my Eclipse project and re-import it because for some reason Eclipse choked on the output folder change at first. – Chriki Feb 16 '16 at 10:49
  • Is this still relevant to gradle as of 2.11? I applied this change, deleted `.project` and `.classpath` and reimported the gradle project and it regenerated .classpath with just the same single output `bin` directory. – Andy Brown Mar 18 '16 at 09:25
1

Setting default output folder to 'build' and test output folder to 'build-test' (tested with Gradle 2.7):

eclipse.classpath {
    defaultOutputDir = file('build')
    file.withXml { n ->
        n.asNode().classpathentry.findAll { it.@path.startsWith('src/test') }
                .each { it.@output = 'build-test' }
    }
}

Resulting .classpath file entries:

<classpathentry kind="output" path="build"/>
<classpathentry kind="src" path="src/main/java"/>
<classpathentry kind="src" path="src/main/resources"/>
<classpathentry kind="src" path="src/test/java" output="build-test"/>
<classpathentry kind="src" path="src/test/resources" output="build-test"/>
Ogmios
  • 646
  • 7
  • 12