I am trying to create an Zip task in a plugin:
class MyPlugin implements Plugin<Project> {
#Override
void apply(Project project) {
Zip buildFunctionArchive = project.tasks.create("buildFunctionArchive", Zip.class) {
archiveClassifier = "yolo"
from(project.getTasksByName("compileJava", true))
}
}
}
But for some reason even though the compileJava task exists in the project form which I use my plugin. When I print debug output I get: NO-SOURCE
2020-10-05T02:16:03.565+1100 [LIFECYCLE] [class org.gradle.internal.buildevents.TaskExecutionLogger] > Task :buildFunctionArchive NO-SOURCE
If I configure the from in my client project:
buildFunctionArchive{
from compileJava
}
This works, and I even see the yolo in the archive name. But if I remove the from compileJava
buildFunctionArchive{
}
As well as removing the configuration all together and running gradle buildFunctionArchive
The task will not create the archive, even though I have configured this in the MyPlugin class, I will get the NO-SOURCE error. What am I missing? I am wanting to define a Zip task in my plugin but am having no luck.
Why is my Spock test not executed and I get zero test results when I execute:
./gradlew clean test
with my TestFX Spock Gradle Project with Openjdk 11?
Here's the zero test results:
My Spock test class gets compiled OK but not executed.
Here's my console:
Working Directory: /home/~/EclipseProjects/gradleTestfxSpock
Gradle user home: /home/~/.gradle
Gradle Distribution: Gradle wrapper from target build
Gradle Version: 5.0
Java Home: /usr/lib/jvm/jdk-11.0.2+9
JVM Arguments: None
Program Arguments: None
Build Scans Enabled: false
Offline Mode Enabled: false
Gradle Tasks: clean test
> Configure project :
Found module name 'mtd'
> Task :clean
> Task :compileJava
> Task :compileGroovy NO-SOURCE
> Task :processResources
> Task :classes
> Task :compileTestJava NO-SOURCE
> Task :compileTestGroovy
> Task :processTestResources
> Task :testClasses
> Task :test
BUILD SUCCESSFUL in 6s
6 actionable tasks: 6 executed
Here's my build.gradle:
plugins {
id 'org.openjfx.javafxplugin' version '0.0.7'
id 'application'
id 'groovy'
}
mainClassName = 'mtd/gradleTestfxSpock.Main'
sourceCompatibility = 11
targetCompatibility = 11
repositories {
jcenter()
}
dependencies {
implementation 'org.testfx:testfx-spock:4.0.15-alpha'
testCompile 'org.testfx:testfx-core:4.0.15-alpha'
testCompile (group: 'org.spockframework', name: 'spock-core', version: '1.3-groovy-2.5')
testCompile ('org.codehaus.groovy:groovy-all:2.5.6')
testRuntime(
'com.athaydes:spock-reports:1.2.7',
'cglib:cglib-nodep:3.2.4'
)
}
javafx {
version = "11.0.2"
modules = [ 'javafx.controls',
'javafx.fxml',
'javafx.web'
]
}
compileJava {
doFirst {
options.compilerArgs = [
'--module-path', classpath.asPath,
'--add-modules', 'javafx.controls',
'--add-modules', 'javafx.fxml',
'--add-modules', 'javafx.web'
]
}
}
test {
doFirst {
jvmArgs = [
'--module-path', classpath.asPath,
'--add-modules', 'ALL-MODULE-PATH',
'--add-exports', 'javafx.graphics/com.sun.javafx.application=org.testfx'
]
}
}
Here's my module-info.java:
module mtd {
requires javafx.controls;
requires javafx.fxml;
requires transitive javafx.graphics;
requires javafx.web;
requires org.testfx;
requires testfx.spock;
opens gradleTestfxSpock to javafx.graphics;
exports gradleTestfxSpock;
}
Here's my Spock test code:
package gradleTestfxSpock;
import org.testfx.framework.spock.ApplicationSpec;
import javafx.stage.Stage
public class MainTest extends ApplicationSpec{
def "Main Test 01"() {
expect:
println("you are in Main test 01");
}
#Override
public void start(Stage arg0) throws Exception {
// TODO Auto-generated method stub
}
}
Here's my JavaFX code:
package gradleTestfxSpock;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("/fxml/sample.fxml"));
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(root, 300, 275));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
and controller:
package gradleTestfxSpock;
public class Controller {
}
Here's my eclipse gradle project structure:
In other eclipse gradle projects I have successfully executed a TestFX Junit4 test without Spock:
and separately I have successfully executed the same Spock Test without TestFX and without JUnit:
I did notice some warnings on this Spock test:
Working Directory: /home/~/EclipseProjects/gradleSpock
Gradle user home: /home/~/.gradle
Gradle Distribution: Gradle wrapper from target build
Gradle Version: 5.0
Java Home: /usr/lib/jvm/jdk-11.0.2+9
JVM Arguments: None
Program Arguments: None
Build Scans Enabled: false
Offline Mode Enabled: false
Gradle Tasks: clean test
> Task :clean
> Task :compileJava
> Task :compileGroovy NO-SOURCE
> Task :processResources NO-SOURCE
> Task :classes
> Task :compileTestJava NO-SOURCE
> Task :compileTestGroovy
> Task :processTestResources NO-SOURCE
> Task :testClasses
> Task :test
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.codehaus.groovy.vmplugin.v7.Java7$1 (file:/home/dm/.gradle/caches/modules-2/files-2.1/org.codehaus.groovy/groovy/2.5.6/6936e700f0fb1b50bac0698ada4347a769d40199/groovy-2.5.6.jar) to constructor java.lang.invoke.MethodHandles$Lookup(java.lang.Class,int)
WARNING: Please consider reporting this to the maintainers of org.codehaus.groovy.vmplugin.v7.Java7$1
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
BUILD SUCCESSFUL in 9s
4 actionable tasks: 4 executed
Conclusion
If TestFX with JUnit works, and Spock alone works, but TestFX with Spock doesn't work then is there something wrong with configuring:
'org.testfx:testfx-spock:4.0.15-alpha'
Any ideas or help greatly appreciated.
ps Forgot to say that I also created the TestFX/Spock project in Netbeans duplicating the eclipse project, and I got the same result!
More Tests
More combinations of testing following Leonard Bruenings very good suggestions in comments below unfortunately didn't work.
My amended module-info.java looks like:
module mtd {
requires javafx.controls;
requires javafx.fxml;
requires transitive javafx.graphics;
requires javafx.web;
requires org.testfx.junit;
requires org.testfx;
requires testfx.spock;
requires spock.core;
requires junit;
opens gradleTestfxSpock to javafx.graphics, org.testfx, testfx.spock, spock.core, junit, org.testfx.junit;
exports gradleTestfxSpock;
}
And I added this to my gradle.build dependencies just in case:
implementation 'org.testfx:testfx-junit:4.0.15-alpha'
Still no joy...
Solution
I found that this plugin was the problem and was stopping successful execution of my Spock test:
`plugins {
id 'org.openjfx.javafxplugin' version '0.0.7'
}`
When I removed it from my build.gradle my TestFX Spock test worked OK.
With this reworked build.gradle and module-info.java I can successfully execute my Spock test:
plugins {
id 'application'
id 'groovy'
}
ext {
moduleName = "mtd"
mainQualifiedClassName = "gradleTestfxSpock.Main"
}
application {
mainClassName = "$moduleName/$mainQualifiedClassName"
}
sourceCompatibility = 11
targetCompatibility = 11
repositories {
jcenter()
}
dependencies {
implementation("org.openjfx:javafx-fxml:11:linux")
implementation("org.openjfx:javafx-web:11:linux")
implementation("org.openjfx:javafx-media:11:linux")
implementation("org.openjfx:javafx-base:11:linux")
implementation("org.openjfx:javafx-graphics:11:linux")
implementation("org.openjfx:javafx-controls:11:linux")
testImplementation ('org.testfx:testfx-spock:4.0.15-alpha')
testImplementation ('org.testfx:testfx-core:4.0.15-alpha')
testImplementation (group: 'org.spockframework', name: 'spock-core', version: '1.3-groovy-2.5')
testImplementation ('org.codehaus.groovy:groovy-all:2.5.6')
testRuntimeOnly (
'com.athaydes:spock-reports:1.2.7',
'cglib:cglib-nodep:3.2.4'
)
}
compileJava {
doFirst {
options.compilerArgs = [
'--module-path', classpath.asPath,
'--add-modules', 'javafx.controls',
'--add-modules', 'javafx.fxml',
'--add-modules', 'javafx.web'
]
}
}
run {
doFirst {
jvmArgs = [
'--module-path', classpath.asPath,
'--patch-module', "$moduleName=" + files(sourceSets.main.output.resourcesDir).asPath,
'--module', mainClassName
]
}
}
I needed to add into the run section of the build.gradle:
'--module', mainClassName
-so that the mainClassName can be found
'--patch-module', "$moduleName=" + files(sourceSets.main.output.resourcesDir).asPath
-to provide access to resource files eg getClass().getResource("/fxml/sample.fxml")
module mtd {
requires javafx.controls;
requires javafx.fxml;
requires transitive javafx.graphics;
requires javafx.web;
exports gradleTestfxSpock;
}
I'm no expert in any of this, which is probably why it's taken me ages and ages to get to this point, but FWIW I thought I might just mention that I have managed to get it working: by "it" I mean: Java 11, JavaFX, TestFX-Spock, and using the javafxplugin. One guy who helped me, José Pereda, appears to recommend using it if you can. So I just thought I'd show my files stripped down to the bare minimum. There's no module-info.java, and I'm using Groovy for my app code as well as for Spock and Gradle.
(stripped-down) build.gradle:
plugins {
id 'java-library'
id 'groovy'
id 'eclipse'
id 'application'
id 'org.openjfx.javafxplugin' version '0.0.8'
}
repositories {
mavenCentral()
}
dependencies {
api 'org.apache.commons:commons-math3:3.6.1'
implementation 'com.google.guava:guava:27.0.1-jre'
// although not using JUnit I think it's best to leave this line
// (included at start by Gradle):
testImplementation 'junit:junit:4.12'
implementation 'org.codehaus.groovy:groovy-all:2.5.8'
testImplementation 'org.spockframework:spock-core:1.2-groovy-2.5'
testImplementation 'org.testfx:testfx-spock:4.0.15-alpha'
}
mainClassName = 'core.App'
group 'Project'
version '1.0'
sourceCompatibility = 11
javafx {
version = "13"
modules = [ "javafx.controls", "javafx.fxml" ]
}
src/main/groovy/core/main.groovy:
package core
// (imports omitted)
public class App extends Application {
void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"))
primaryStage.title = "Hello World"
primaryStage.scene = new Scene(root, 300, 275)
primaryStage.show()
}
def main(String[] args) {
launch( App.class, args)
}
}
class Controller {
#FXML
TextField inputField
#FXML
Label label
#FXML
Button applyButton
public void applyButtonClicked () {
label.text = inputField.text
}
}
src/test/groovy/core/test.groovy:
package core
// (imports omitted)
class FuncSpec extends ApplicationSpec {
void init() throws Exception {
FxToolkit.registerStage { new Stage() }
}
def cleanup() {
FxToolkit.hideStage()
// as explained for org.testfx.robot.KeyboardRobot, passing an
// empty array of the appropriate type releases all keys/mouse buttons
// NB this is how you create an empty typed array in Groovy
release(new KeyCode[0])
release(new MouseButton[0])
}
#Override
public void start(Stage stage) throws Exception {
Parent mainNode = FXMLLoader.load( App.class.getResource("sample.fxml"))
stage.scene = new Scene(mainNode)
stage.show()
stage.toFront()
}
def "test English input"(){
when:
Label label = lookup("#label").query()
clickOn("#inputField")
write("This is a test!")
clickOn("#applyButton")
then:
label.text == "This is a test!"
}
}
src/main/resources/core/sample.fxml // NB note inclusion of "core" (package name)
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<GridPane alignment="center" hgap="10" vgap="10"
xmlns:fx="http://javafx.com/fxml/1"
xmlns="http://javafx.com/javafx/8.0.111"
fx:controller="core.Controller">
<children>
<TextField layoutX="15.0" layoutY="25.0" fx:id="inputField" />
<Label layoutX="15.0" layoutY="84.0" text="TEXT GOES HERE"
fx:id="label" />
<Button layoutX="124.0" layoutY="160.0" mnemonicParsing="false"
text="Apply" onAction="#applyButtonClicked" fx:id="applyButton" />
</children>
</GridPane>
I am creating a Spring Boot web application that will utilise Kafka. I have created a trivial controller that will return a string.
// src/main/groovy/RootController.groovy
import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.ResponseBody
/**
* Controller to serve the index page
*/
#Controller
class RootController {
/**
* Serves the index page
*
* #return A string to appear on the index page
*/
#GetMapping("/")
#ResponseBody
String loadPage() {
return "root/index"
}
}
I have a unit test for this that uses Spock.
// src/test/groovy/RootControllerTest.groovy
import org.springframework.test.context.web.WebAppConfiguration
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.setup.MockMvcBuilders
import spock.lang.Specification
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
#WebAppConfiguration
class RootControllerTest extends Specification {
MockMvc mockMvc
// Setup for tests happens here˚
void setup() {
mockMvc = MockMvcBuilders.standaloneSetup(new RootController()).build()
}
// Cleanup for tests happens here
void cleanup() {}
def "LoadPage should return the string root/index"() {
when: 'the loadPage endpoint is hit'
def response = mockMvc.perform(get("/"))
then: 'the method loadPage returns the string root/index'
response.andExpect(status().isOk())
.andExpect(content().string("root/index"))
}
}
When run, this test passes. I have the following build.gradle file that will build and run the tests.
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:1.5.1.RELEASE")
}
}
plugins {
id 'org.springframework.boot' version '1.5.1.RELEASE' // Spring Boot
}
group 'dummy'
version '1.0-SNAPSHOT'
task wrapper(type: Wrapper) {
gradleVersion = '3.1'
distributionUrl = "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip"
}
apply plugin: 'groovy'
sourceCompatibility = 1.8
def kafkaVersion = '0.10.0.0'
repositories {
maven {
name "confluent"
url "http://packages.confluent.io/maven/"
}
mavenCentral()
}
dependencies {
compile "org.codehaus.groovy:groovy-all:2.4.4" // Groovy version
compile "org.springframework.boot:spring-boot-starter-thymeleaf" // For thymeleaf
//compile "org.apache.kafka:kafka_2.11:${kafkaVersion}-cp1" // Kafka, as produced by confluent, hence -cp1 suffix
// Testing dependencies go here
testCompile "org.springframework:spring-test:4.3.6.RELEASE"
testCompile "org.spockframework:spock-core:1.0-groovy-2.4" // Spock (Mandatory)
}
However, when I uncomment the Kafka dependency, the build fails like this:
$ gradle clean test
:clean
:compileJava UP-TO-DATE
:compileGroovy
:processResources UP-TO-DATE
:classes
:compileTestJava UP-TO-DATE
:compileTestGroovy
:processTestResources UP-TO-DATE
:testClasses
:test
RootControllerTest > LoadPage should return the string root/index FAILED
java.lang.ExceptionInInitializerError at RootControllerTest.groovy:21
Caused by: java.lang.IllegalStateException at RootControllerTest.groovy:21
1 test completed, 1 failed
:test FAILED
FAILURE: Build failed with an exception.
What is it that is causing the failure please?
I'm trying to compile (with gradle) and execute (with the java 1.8 runtime) a small groovy program (see helloWorld.groovy, below).
But when I try to invoke it, I get Error: Could not find or load main class helloWorld
What am I missing?
compile:
lexu> gradle clean jar
:clean
:compileJava UP-TO-DATE
:compileGroovy UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:jar
BUILD SUCCESSFUL
Total time: 0.555 secs
execute:
lexu> java -jar ./build/libs/helloWorld.jar
Error: Could not find or load main class helloWorld
helloWorld.groovy:
class helloWorld {
static void main(String[] args) {
println('Hello World');
}
}
build.gradle:
apply plugin: 'groovy'
apply plugin: 'application'
mainClassName = "helloWorld"
archivesBaseName = 'helloWorld';
configurations {provided; inlib;}
repositories {mavenCentral()}
dependencies {compile 'org.codehaus.groovy:groovy-all:2.4.7'}
jar {
manifest {
attributes(
'Class-Path': configurations.compile.collect { it.getName() }.join(' '),
'Main-Class': 'helloWorld'
)
}
}
There are a couple of issues with your setup.
Not breaking, but worth mentioning here: Convention states your filenames and classes should be uppercase: HelloWorld.groovy
gradle assumes your source files to be under src/main/java or in this case, src/main/groovy. You can configure it according to your preferences with gradle groovy plugin - project layout:
sourceSets {
main {
groovy {
srcDirs = ['src/groovy']
}
}
}
You need to include all runtime dependencies for groovy in your jar-archive. For this, let's use use an extended task called uberjar.
build.gradle:
apply plugin: 'groovy'
apply plugin: 'application'
mainClassName = "HelloWorld"
archivesBaseName = 'HelloWorld';
configurations {provided; inlib;}
repositories {mavenCentral()}
dependencies {compile 'org.codehaus.groovy:groovy-all:2.4.7'}
task uberjar(type: Jar,dependsOn:[':compileJava',':compileGroovy']) {
from files(sourceSets.main.output.classesDir)
from configurations.runtime.asFileTree.files.collect { zipTree(it) }
manifest {
attributes 'Main-Class': mainClassName
}
}
HelloWorld.groovy:
class HelloWorld {
static void main(String[] args) {
println('Hello World');
}
}
I have a simple code which runs with TestNG, but I am unable to run the same with Gradle, as it says no main method is found, which is, well, not surprising since I am using annotations.
But in such a scenario, how to run the code if I must use Gradle.
Kindly note, I am very new to Gradle, and do not harbour much knowledge about the same.
Code:
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
public class tryTestNG
{
#BeforeClass
public void setup()
{
System.out.println("I am in Setup");
}
#Test
public void test()
{
System.out.println("I am in Test");
}
#AfterClass
public void tearDown()
{
System.out.println("I am in tearDown");
}
}
The above code runs perfectly with TestNG Library. However not with Gradle.
Here is my Gradle Build setup:
apply plugin: 'java'
apply plugin: 'application'
mainClassName = 'tryTestNG'
sourceCompatibility = 1.7
targetCompatibility = 1.7
version = '1.0'
repositories {
mavenCentral()
}
test {
useTestNG()
}
dependencies {
compile group: 'org.testng', name: 'testng', version: '6.9.10'
}
The Gradle returns that there is no Main Method.
Working Directory: /home/avirup/MyWorkspace/JavaWorkspace/TestNGGradle
Gradle User Home: /home/avirup/.gradle
Gradle Distribution: Gradle wrapper from target build
Gradle Version: 2.9
Java Home: /usr/lib/jvm/java-8-oracle
JVM Arguments: None
Program Arguments: None
Gradle Tasks: run
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:runError: Main method not found in class tryTestNG, please define the main method as:
public static void main(String[] args)
or a JavaFX application class must extend javafx.application.Application
FAILED
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':run'.
> Process 'command '/usr/lib/jvm/java-8-oracle/bin/java'' finished with non-zero exit value 1
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
BUILD FAILED
Total time: 0.514 secs
Thanks for your help.
There's no need to use application plugin to run tests.
The build.gradle should be:
apply plugin: 'java'
sourceCompatibility = 1.7
targetCompatibility = 1.7
version = '1.0'
repositories {
mavenCentral()
}
test {
useTestNG()
}
dependencies {
compile group: 'org.testng', name: 'testng', version: '6.9.10'
}
And the command: gradle test. Also, put tryTestNG under src/test/java and name it with capital letter.
Here is a demo.
Also mind that println statements from tests won't be visible in console. To view them navigate to test report. It's under: <project_dir>/build/reports/tests/index.html.