I am trying to do a POC for making a different directory approach for keeping unit, integration acceptance test, etc. I have seen many blogs where people creates folder along with main and test, but my intention is not to segregate but to consolidate those under the test folder itself. Like to know your views on this approach, I'm not sure if this has any pitfalls
For the above scenario, I have created three folders under the test folder in my spring boot application like as shown below for keeping unit test, integration test, and acceptance test.
My Gradle configuration is like as shown below
sourceSets {
test {
java {
srcDirs += ['src/test/integration/java', 'src/test/unit/java', 'src/test/acceptance/java']
}
resources {
srcDirs += ['src/test/integration/resources', 'src/test/unit/resources', 'src/test/acceptance/resources']
}
}
}
The above configuration is working fine, the only problem is I don't know how we can comply and build the files using
./gradle test -----------> builds unit, integration, acceptance tests & generates report
./gradle unit -----------> builds only unit tests & generates report
./gradle integration ----> builds only integration tests & generates report
./gradle acceptance -----> builds only acceptance tests & generates report
I made gradle configurations for both cases: segregated and consolidated tests. You can see that in the case of consolidated tests the config is pretty simple, and classpath is always one src/test/java, and this approach provides a fairly simple human understanding of where the tests are located, for segregated tests the config is complicated and requires fine tuning, can arise classes and resources conflicts if no tests name patterns, and that case has the critical meaning for which type of writing tests you will chose.
This is configuration for multi test folders:
configurations {
integrationImplementation.extendsFrom(testImplementation)
integrationRuntimeOnly.extendsFrom(testRuntimeOnly)
unitRuntimeOnly.extendsFrom(testRuntimeOnly)
unitImplementation.extendsFrom(testImplementation)
}
sourceSets {
integration {
compileClasspath += sourceSets.main.output
runtimeClasspath += sourceSets.main.output
java {
srcDir file('src/test/integration/java')
}
resources {
srcDir file('src/test/integration/resources')
}
}
unit {
compileClasspath += sourceSets.main.output
runtimeClasspath += sourceSets.main.output
java {
srcDir file('src/test/unit/java')
}
resources {
srcDir file('src/test/unit/resources')
}
}
}
test {
useJUnitPlatform()
testClassesDirs += sourceSets.integration.output.classesDirs
testClassesDirs += sourceSets.unit.output.classesDirs
classpath += sourceSets.integration.runtimeClasspath
classpath += sourceSets.unit.runtimeClasspath
beforeTest { descriptor ->
logger.lifecycle("Running test: $descriptor")
}
testLogging {
events "PASSED", "FAILED", "SKIPPED"
}
}
task integration(type: Test) {
useJUnitPlatform()
testClassesDirs += sourceSets.integration.output.classesDirs
classpath += sourceSets.integration.runtimeClasspath
beforeTest { descriptor ->
logger.lifecycle("Running test: $descriptor")
}
testLogging {
events "PASSED", "FAILED", "SKIPPED"
}
}
task unit(type: Test) {
useJUnitPlatform()
testClassesDirs = sourceSets.unit.output.classesDirs
classpath = sourceSets.unit.runtimeClasspath
beforeTest { descriptor ->
logger.lifecycle("Running test: $descriptor")
}
testLogging {
events "PASSED", "FAILED", "SKIPPED"
}
}
In case you have a single folder for all test types, your configuration will be:
task integrationSingleFolder(type: Test) {
filter {
includeTestsMatching "*IT"
}
useJUnitPlatform()
testLogging {
events "PASSED", "FAILED", "SKIPPED"
}
}
task unitSingleFolder(type: Test) {
filter {
includeTestsMatching "*Test"
}
useJUnitPlatform()
testLogging {
events "PASSED", "FAILED", "SKIPPED"
}
}
You can create new gradle tasks in your build.gradle extending from original test task and configure respective src and resource dirs. Something like this
task unit(type: Test) {
// your configuration
include 'org/foo/unit/**'
}
and make test.DependsOn(unit) to execute with test task.
Related
i'm currently setting up a new project using java, gradle and spring boot. I'm struggling to create my planned test setup. My goal is to have separate test commands and directories located IN the test package.
What I have
I have looked into gradle sourceSets and was able to create two new sourceSets for unit and component tests. The problem I face is that I don't know if there is any way to defined the path of the sourcesets so they are inside the test directory.
src
-- main
-- test
-- unit
-- component
What I want
I just want the default test sourceSet to behave like a normal directory which itself contains my two testing sourceSets like this:
src
-- main
-- test
---- unit
---- component
Is this possible and also would this be 'against the convention' or something like that?
Any pointers are appreciated!
Basically, you should be able to set paths to your source sets any way you want to. Something like this should work:
sourceSets {
test {
java {
srcDirs = ['test/unit']
}
resources {
srcDirs = ['test/unit']
}
}
testComponent {
java {
srcDirs = ['test/component']
}
resources {
srcDirs = ['test/component']
}
}
}
Unfortunately I'm not sure if you can change the names of default source sets.
Keep in mind that you will have to provide more configuration for your new source set, e.g. create new Test task:
task testComponent(type: Test) {
testClassesDirs = sourceSets.testComponent.output
classpath += sourceSets.main.output
}
And also manage its dependencies separately or extend them from already existing configuration:
configurations {
testComponentCompile.extendsFrom(testCompile)
testComponentRuntime.extendsFrom(testRuntime)
}
As for the convention: it's best to stick to provided defaults, but if you have the need for different structure then it's also fine. Most common case for test separation is probably when you want split unit and integration test in order to be able to run them individually.
I am trying to distinguish my tests into Unit- and Integration tests.
My idea was to use the new JUnit5 Annotation #Tag("unit") which works nicely for my JUnit tests, but I cannot get it to work with Spek.
What I currently have is my class:
data class MyObject(val value: Int)
My tests:
#Tag("unit")
object MyObjectTest {
#Test
fun checkEquality() {
val o1 = MyObject(1)
assertEquals(o1, o1)
}
}
With my build.gradle having:
task utest(type: Test) {
outputs.upToDateWhen { false }
useJUnitPlatform {
includeEngines 'junit-jupiter', 'junit-vintage', 'spek'
includeTags 'unit'
excludeTags 'performance', 'integration', 'functional'
}
testLogging {
events "passed", "skipped", "failed"
}
}
When I execute utest, this works.
However when doing the same with Spek:
#Tag("unit")
object MyObjectSpek : Spek({
given("an Object") {
val o1 = MyObject(1)
it("should be equal to itself") {
assertEquals(o1, o1)
}
}
})
What happens is if I run the gradle task utest it only executes the methods from MyObjectTest and does not execute the tests for MyObjectSpek
Any ideas on how to integrate Spek with JUnit5 Tags or another idea to seperate unit tests and integration tests?
today I ran exactly into the same problem. I had to separate tests into 3 sections: Unit, Service (testing REST API) and Integration (WebDriver).
Disclamer: this guide is applicable for any testing framework, not only to Spek. Gradle 4.6 or newer is required to run this.
Separate test source set into source sets
In my example they would be:
src/test — for Unit tests (you already have it)
src/serviceTest — for Service tests
src/integrationTest — for Integration tests
all these sets should have standard source set structure. Create these folders inside your project and move your packages to corresponding source sets.
When it is done add to build.gradle before dependency section following lines:
sourceSets {
integrationTest {
compileClasspath += main.output
runtimeClasspath += main.output
}
serviceTest {
compileClasspath += main.output
runtimeClasspath += main.output
}
}
configurations {
integrationTestCompile.extendsFrom testCompile
integrationTestRuntime.extendsFrom testRuntime
serviceTestCompile.extendsFrom testCompile
serviceTestRuntime.extendsFrom testRuntime
}
After you do this your IDE (I suppose you use Idea) should reindex build.gradle and recognize source sets. You may have errors in your new source sets because they do not see each other sources. That's correct, because these source sets intended to run independently, and should not be a problem.
Separate dependencies to appropriate configurations (Optional)
By default serviceTest and integrationTest inherit all test dependencies, but if you need to move something specific to certain configuration out of common scope, you can do this here.
In my case WebDriver is quite heavy and I do not need it anywhere except integration testing.
dependencies {
// available for all scopes
testCompile "org.jetbrains.spek:spek-api:$spekVersion"
testRuntime "org.jetbrains.spek:spek-junit-platform-engine:$spekVersion"
testCompile "org.junit.platform:junit-platform-launcher:$junitPlatformVersion"
// compiles only for integrationTest
integrationTestCompile "org.seleniumhq.selenium:selenium-java:3.11.0"
integrationTestCompile "org.seleniumhq.selenium.fluent:fluent-selenium:1.19"
}
Setup execution order
We will need to add gradle task of Test type and setup it. You can have different settings for different test tasks.
task serviceTest(type: Test) {
// Runs tests from src/serviceTest
testClassesDirs = sourceSets.serviceTest.output.classesDirs
classpath = sourceSets.serviceTest.runtimeClasspath
}
// Setup serviceTest task
serviceTest {
// Uncomment this if you need to skip tests from the set after first failure. Since Gradle 4.6
//failFast = true
// Enable some logging
testLogging {
events "PASSED", "FAILED", "SKIPPED"
}
// Enable JUnit5 tests
useJUnitPlatform {
}
}
Do the same for integrationTest.
Finally, setup the dependencies and order of execution:
// Make service tests run during gradle check
check.dependsOn serviceTest
check.dependsOn integrationTest
// Make service tests run after unit tests
serviceTest.mustRunAfter test
// Make integration tests run after service tests
integrationTest.mustRunAfter serviceTest
Conclusion
You will get:
Chain of Unit -> Service -> Integration test suites running in strict order;
If you will get a test failure (regardless of failFast option) in one test suite, the remaining won't run and waste resources;
Ability to run from console each suite separately within execution of gradle <task>.
Additional resources:
Here is the sample build.gradle that implements this setup with Spek
Release notes of gradle 4.6 version introducing a lot of stuff related to testing.
Another thing to consider when using IntelliJ and it having dependency problems with the new source sets, add this to your build.gradle:
apply plugin: 'idea'
idea {
module {
testSourceDirs += project.sourceSets.unitTest.kotlin.srcDirs
testSourceDirs += project.sourceSets.unitTest.resources.srcDirs
testSourceDirs += project.sourceSets.integrationTest.kotlin.srcDirs
testSourceDirs += project.sourceSets.integrationTest.resources.srcDirs
testSourceDirs += project.sourceSets.functionalTest.kotlin.srcDirs
testSourceDirs += project.sourceSets.functionalTest.resources.srcDirs
}
}
Sorry, if this isn't enough information, new to gradle, let me know if more is needed.
I have a log4j2.xml in a config folder that I define in a gradle.properties file.
project.ext.logConfigFile="./config/log4j2.xml"
The structure of my project
project
src
test
lib
config
log4j2.xml
Now when I run gradle test, it says that it can't find the log4j2 configuration.
This is what my build.gradle file looks like..
sourceSets {
test {
java {
srcDirs = ["test"]
}
}
main {
java {
srcDirs = ["src"]
}
}
}
test {
testLogging {
outputs.upToDateWhen {false}
showStandardStreams = true
events "PASSED", "STARTED", "FAILED", "SKIPPED"
afterSuite { desc, result ->
if (!desc.parent) { // will match the outermost suite
println "Results: ${result.resultType} (${result.testCount} tests, ${result.successfulTestCount} successes, ${result.failedTestCount} failures, ${result.skippedTestCount} skipped)"
}
}
}
}
Been dinking around with things I found on the internet, that's why all those statements in the "testLogging" part. But basically, I have LOGGER.info() statements that aren't coming up because it defaults to errors only.
ERROR StatusLogger No log4j2 configuration file found. Using default configuration: logging only errors to the console.
Can I tell gradle test exactly where to go for my log4j2.xml? Something like this? Though I don't think this works....
tasks.withType(Test) {
systemProperty 'log4j.configuration', logConfigFile
}
A little stuck right now. Thanks!
Just put it in src/test/resources. That comes first on the classpath during testing. Then configure log4j to load from classpath, not a file path; I think that’s the default behavior anyway
Believe I solved the problem, you just define where the resources are in the sourceSets also. It will look there for the log4j2.xml.
sourceSets {
test {
java {
srcDirs = ["test"]
}
resources {
srcDirs = ["config"]
}
}
main {
java {
srcDirs = ["src"]
}
}
}
I have some slow jUnit tests that I typically skip during a build using a SlowTests category, e.g.
myjava.java
package com.mycompany.package;
public interface SlowTests { }
public class MyTests {
#Category(SlowTests.class)
#Test
public final void testSomethingTakingALongTime() {
// Some test that takes a while (say over 10 seconds) to run.
}
}
build.gradle
// Gradle stuff...
test {
useJUnit {
excludeCategories 'com.mycompany.package.SlowTests'
}
}
// More gradle stuff...
However, I would like to include these slow running tests before doing a the publish task.
Any ideas on how I might be able to achieve this?
Would just adding a second task that publish depends on be the easiest solution?
My project has both Java (N files) and Groovy code (1 file only). Java compile depends upon this single Groovy file's class file for Java compilation (compileJava task to succeed).
When I don't use src/java as one of the srcDir within main>groovy> sourceSet section, then I get an error saying class/symbol not found which is in the groovy file/class. In ANT, it's easy that we are calling compile-groovy target first, before calling compile-java target but the same in Gradle is what I'm trying to find.
I read some posts and found that if I make main>java section NULL and specify srcDir for main>java which is src/java inside main>groovy sourceSet section, then it compiles fine.
My ?s:
1. Is there any other way to do? for ex, the following should work:
compileJava {
dependsOn compileGroovy
}
though, this goes to an infinte loop.
OR
what about using doFirst for compileJava task:
compileJava {
doFirst {
compileGroovy
}
}
this doesn't work either.
build.gradle This works, but compileJava in one sense becomes useless here even though the source code has N no. of java files in the src/java or src/java-test etc tree. I know this build script is working but logically it might bring some confusion to the developer if s/he is not familiar why sourceSet for Groovy MUST have "src/java" as its srcDir value.
apply plugin: 'java'
apply plugin: 'groovy'
sourceSets {
main {
groovy {
srcDir 'src/groovy'
srcDir 'src/java'
}
java {
//The following needs to be commented out OR Gradle will always pick compileJava before compileGroovy
//srcDir 'src/java'
//srcDir 'src/java-test'
}
}
test {
groovy {
srcDir 'test/groovy'
}
java {
srcDir 'test/java'
}
resources {
srcDir 'test/resources'
srcDir 'conf'
}
}
integrationTest {
groovy {
srcDir 'src/groovy-test'
}
java {
srcDir 'src/java-test'
}
resources {
srcDir 'test/resources'
srcDir 'conf'
}
}
}
Other links:
How to make Gradle compile Groovy tests before Java tests
The Groovy (base) plugin makes GroovyCompile tasks depend on the corresponding JavaCompile tasks because it's more common to call from Groovy into Java than the other way around. If you need it the other way around (or both ways), joint compilation is a good solution. Here is a somewhat improved (over your version) joint compilation setup:
sourceSets {
main {
groovy {
// override the default locations, rather than adding additional ones
srcDirs = ['src/groovy', 'src/java']
}
java {
srcDirs = [] // don't compile Java code twice
}
}
}
If you prefer separate compilation with Java->Groovy dependencies only, something like the following should work:
// since you aren't using the default locations
sourceSets {
main {
groovy {
srcDirs = ['src/groovy']
}
java {
srcDirs = ['src/java']
}
}
}
// remove GroovyCompile->JavaCompile task dependencies
tasks.withType(GroovyCompile) {
dependsOn = []
}
// add JavaCompile->GroovyCompile task dependencies
tasks.withType(JavaCompile) { task ->
dependsOn task.name.replace("Java", "Groovy")
}
Because a JavaCompile task and its corresponding GroovyCompile task write to the same output directory, Java compilation will now have the compiled Groovy code on its compile class path.
PS: Calling a task from another task is not supported, and bad things can happen if you try. Instead, you should always work with task relationships (dependsOn, finalizedBy, mustRunAfter, shouldRunAfter).