Derek
Derek

Reputation: 93

Create a flat directory structure with both source and tests in Intellij/Gradle?

I'm using Kotlin with Intellij and Gradle, all of them for the first time (my original goal was to implement a personal project while learning Kotlin, I figured it would be best to go with the "full" environment). I would like to organize my project with a simple flat directory structure where all my code and tests are in the same directory. So I would like it to look something like this:

project
|-src
  |-MyClass.kt
  |-MyClassTest.kt
  |-MyOtherClass.kt
  |-MyOtherClassTest.kt

I've tried to look at the documentation for both Gradle and Intellij, but to be honest I can't make heads or tails of any of it.

The Kotlin plugin in Gradle appears to have a default directory structure, but it can be changed. So I've tried to adding the following to the build.gradle file based on some code snippets I found in my searching:

sourceSets {
    main {
        kotlin {
            srcDirs = ['src']
            exclude('*Test.kt')
        }
    }
    test {
        kotlin {
            srcDirs = ['src']
            include('*Test.kt')
        }
    }
}

This seems to put all of the code, both source and tests, into main. The source works fine, but the tests complain of undefined references when importing the "kotlin.test" package. Here is my complete build.gradle file (all of this is pre-generated by IntelliJ except the sourceSets):

buildscript {
    ext.kotlin_version = '1.2.41'

    repositories {
        mavenCentral()
    }
    dependencies {
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
}

version '1.0-SNAPSHOT'

apply plugin: 'kotlin'

sourceSets {
    main {
        kotlin {
            srcDirs = ['src']
            exclude('*Test.kt')
        }
    }
    test {
        kotlin {
            srcDirs = ['src']
            include('*Test.kt')
        }
    }
}

repositories {
    mavenCentral()
}

dependencies {
    compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
}

compileKotlin {
    kotlinOptions.jvmTarget = "1.8"
}
compileTestKotlin {
    kotlinOptions.jvmTarget = "1.8"
}

And then Intellij has Project Structure settings, which I can use to mark the src folder as either Source or Test, but this doesn't appear to let you set it as both. I'm also confused by what the Module window shows me. At the top level it has "myproject" with four blue boxes and "myproject" with one blue box, and inside the first these is "myproject_main" and "myproject_test", both with one blue box. I don't understand the difference between the one blue box and the four blue boxes, or how this stuff relates to my directory structure (maybe it doesn't?). And then I'm not sure how any of these settings interact with Gradle.

All in all, I'm very confused, and I don't even know if my problem is with Gradle settings, Intellij settings, or both. I thought this would be a simple task but it's turned into a massive rabbit hole. Ideally I would like to create the directory structure I've described, but I would be happy with even just a good explanation of what all these settings I've been messing with do, because the resources I've found online haven't helped me understand at al.

Upvotes: 1

Views: 863

Answers (1)

yole
yole

Reputation: 97188

The key thing you need to understand is that IntelliJ IDEA requires each directory to belong to a single source root, of a single type. Because of that, even though Gradle allows you to say that files matching a specific pattern should be considered test sources and files not matching this pattern should be production sources, such a configuration cannot be properly reflected in the IntelliJ IDEA project structure.

Another key thing is that you should never change the project structure manually in a project that has been imported from Gradle. This will lead to different behavior for IDE and command line builds, and it's quite likely that your changes will be lost during subsequent project resyncs.

The thing with four blue boxes is called a module group, and the thing with one blue box is called a module. When importing a Gradle project into IntelliJ IDEA, it creates a module group for each of the Gradle modules, and then creates three modules under each module group (for the Gradle module as a whole, for its production sources, and for its test sources).

If you want to keep your code in a single directory, then basically you need to treat all of your code as production sources. Just don't specify any settings for the test roots and use the regular "compile" scope for all dependencies.

Alternatively, if you want to ship your application to users and don't want to ship the compiled tests as part of it, you should keep the production and test sources in separate directories. You can still reduce nesting if you create a structure like the following:

project
|-src
  |-MyClass.kt
  |-MyOtherClass.kt
|-testSrc
  |-MyClassTest.kt
  |-MyOtherClassTest.kt

Upvotes: 0

Related Questions