I have started new java 11, spring boot 2.3.1 project in IntelliJ.
I wanted to add spock to dependencies however I am facing a problem when trying to run example test case.
org.junit.runners.model.InvalidTestClassError: Invalid test class 'com.example.SpockSpec':
1. No runnable methods
This is list of my dependencies:
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
testImplementation "org.codehaus.groovy:groovy-all:$groovyVersion"
testImplementation 'org.spockframework:spock-core:2.0-M2-groovy-3.0'
}
(groovy version is 3.0.0)
this is the spec:
class SpockSpec extends Specification {
def "result should be true"(){
given:
boolean a = false;
boolean b = true;
when:
boolean result = a || b;
then:
result == true;
}
}
Workaround is to add JUnit's #Test annotation before the method but I would prefer not to do that.
Also - when I do so testing only works when I run whole class.
How can I resolve the error?
Spock 1 is based on JUnit 4, but Spock 2 runs on the JUnit 5 platform, implementing its own engine (not JUnit Jupiter). So maybe you want to first read some documentation and then upgrade to JUnit 5 (not vintage). Right on top of the manual in chapter "getting started" there is a subchapter "Spock example project".
Related
I'm trying to deal with annotation processors. I followed tutorials. Here is my code:
#ExampleAnnotation
private void someMethod(){
System.out.println("hi");
}
#Retention(RetentionPolicy.SOURCE)
#Target(ElementType.METHOD)
public #interface ExampleAnnotation {
}
#SupportedAnnotationTypes("org.example.ExampleAnnotation")
public class Processor extends AbstractProcessor {
#Override
public boolean process(Set<? extends TypeElement> anots, RoundEnvironment roundEnvironment) {
anots.forEach(System.out::println);
return true;
}
}
I created META-INF/SERVICES/javax.annotation.processing.Processor
and registered my processor: org.example.Processor. It seems like everything is OK, but block of code in the processor just dont start. I have no idea what is wrong. P.S.: I use Gradle and Java 11.
i fixed my issue and decided to make a little step-by-step tutorial to create simple JAP:
Java-Annotation-Processor-guide
This is guide for annotation processing using gradle
I spent 3 days trying to deal with it, but with this little tutorial you will made JAP in 5 minutes.
sorry for bad english :)
So, first you should do is create gradle subproject for your project:
create project itself with gradle init or intellij
add to your main project's settings.gradle file this line include "*your subproject name, lets say:*annotation-processor"
Congrats, now let go to the processor itself
here is so much tutorials on the web about this part, so you can read something like this https://www.baeldung.com/java-annotation-processing-builde,
or this (Если ты русский): https://habr.com/ru/company/e-legion/blog/206208/
Final step is very easy - you will add your annotation processor to main project.
!!! instead of annotation-processor you should use your subproject name !!!
kotlin (or java + kotlin) -> {
add this plugin to your plugins: id "org.jetbrains.kotlin.kapt" version "1.7.21"
add this to your dependencies:
kapt project('annotation-processor')
compileOnly project('annotation-processor')
}
java -> {
add this to your dependencies:
annotationProcessor project('annotation-processor')
compileOnly project('annotation-processor')
}
(or you can read this on github: https://github.com/Blu3cr0ss/Java-Annotation-Processor-guide)
Not sure if this is a VSCode Java extension issue or not. We're trying to have conditional dependencies included depending on a specific Spring Boot profile we're passing in as a command line argument. It seems that when conditions are statically analysable, the Java language server is able to correctly provide intellisense, but when they are not, it fails. Here's an example, where when we use true/false, intellisense works, but when we use the commented out lines, it doesn't. I imagine this is the expected behaviour, and it still compiles fine, I'm just wondering if there's a way around making it work.
def profiles = (project.hasProperty('profiles')
? project.property('profiles')
: "local,https").split(",")
dependencies {
// if (profiles.contains("local") && !profiles.contains("tc")) {
if (false) {
runtimeOnly "com.h2database:h2"
}
else {
implementation "org.postgresql:postgresql:$DB_DRIVER_VERSION"
// if (profiles.contains("tc")) {
if (true) {
implementation "org.testcontainers:postgresql:$TESTCONTAINERS_VERSION"
runtimeOnly "org.testcontainers:testcontainers:$TESTCONTAINERS_VERSION"
testCompile "org.testcontainers:junit-jupiter:$TESTCONTAINERS_VERSION"
}
}
}
Here's an example of what happens in a test file
// this import fails with intellisense, since the dependency is
// not included when using conditional dependencies based on CLI arguments
import org.testcontainers.junit.jupiter.Testcontainers;
#Profile("tc")
// #Testcontainers
public class SampleIntegrationTest {
I should reiterate though, that this isn't a problem during normal build/run, it's just intellisense that doesn't work very well.
I have java and groovy tests in the project. I would like to split tests into groups.
For this I am trying to use #Tag annotation on class level.
For java classes it works perfectly but Groovy classes seem to be not affected by #Tag annotation at all.
Junit version: junit-jupiter-api:5.2.0
Spock version 1.1-groovy-2.4
Groovy version: groovy-all:2.4.15
Java version: 8
package com.myproject.service
import org.junit.jupiter.api.Tag
import spock.lang.Specification
#Tag("MyTestGroup")
class MyTest extends Specification {
def 'test me'() {
expect:
1 == 5
}
}
And in build.gradle:
test {
useJUnitPlatform {
excludeTags 'MyTestGroup'
}
}
tasks.register("MyTestGroup", Test) {
useJUnitPlatform {
includeTags 'MyTestGroup'
}
check.dependsOn it
MyTestGroup.shouldRunAfter test
}
And I run tests using: ./gradlew check
Does anybody know how to make #Tag working with Groovy files?
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
}
}
Let's say we have the following test code:
import org.jetbrains.annotations.NotNull;
import org.junit.Test;
public class NullTest {
#Test
public void testNull() {
doNothing(null);
}
private #NotNull String doNothing(#NotNull String value) {
return value;
}
}
The test will pass when running gradle test directly or if IDEA delegates the action to gradle.
But it will fail with IllegalArgumentException: Argument for #NotNull parameter 'value' must not be null exception if it runs using IDEA runner (not delegated to gradle).
The question is: how to fail the test running it with gradle?
The easiest solution I have found is to apply org.jetbrains.intellij plugin.
Because among other things this plugin "patches compile tasks to instrument code with nullability assertions".
apply plugin: 'org.jetbrains.intellij'
intellij {
instrumentCode = true
downloadSources = false
}
Try adding the following to your dependencies. It worked for me.
compile 'org.jetbrains:annotations:13.0'
With this code - no way, because you use annotations from org.jetbrains.annotations.*, that use only in intellij idea tests runner. For gradle, annotation #NotNull (or #Nullable) says nothing. Maven also doesn't see this annotation. I can advise you use Objects.requireNonNull(T obj) for null checking.
We found that Lombok's #NonNull works better. But you need to configure IDEA to prefer this one during nullability-related analysis and generation
Adding the following dependency worked for me:
compile group: 'org.jetbrains', name: 'annotations', version: '15.0'
Run the 'dependencies' task & push the refresh button in Gradle.