Run a process before tests

I have migrated my pom.xml over to build.gradle, but recently added process-exec-maven-plugin to start a ruby script before running the tests.

Is there a gradle plugin for the equivalent maven plugin?
Or do I really need a plugin for this? What I need is to start a new ruby process prior to running the tests, and stop this process after all the tests.

I used the process-exec-maven-plugin instead of exec-maven-plugin since I needed the ruby process to run in a separate process, otherwise it would halt the regular building.

        <plugin>
            <groupId>com.bazaarvoice.maven.plugins</groupId>
            <artifactId>process-exec-maven-plugin</artifactId>
            <version>0.4</version>
            <executions>
                <!--Start process-->
                <execution>
                    <id>start-ruby-mock</id>
                    <phase>generate-test-sources</phase>
                    <goals><goal>start</goal></goals>
                    <configuration>
                        <workingDir>${basedir}/src/test/ruby/server_mock</workingDir>
                        <arguments>
                            <argument>ruby</argument>
                            <argument>${basedir}/src/test/ruby/server_mock/server_mock.rb</argument>
                        </arguments>
                    </configuration>
                </execution>
                <!--Stop Process-->
                <execution>
                    <id>stop-ruby-mock</id>
                    <phase>post-integration-test</phase>
                    <goals><goal>stop-all</goal></goals>
                </execution>
            </executions>
        </plugin>

Use https://docs.gradle.org/current/javadoc/org/gradle/api/tasks/Exec.html.

Task dependencies https://docs.gradle.org/current/userguide/tutorial_using_tasks.html#sec:task_dependencies

Example:

apply plugin: "java"

task startRubyMock(type: Exec) {

  executable "sh"
  args "-c", "echo '==> Ruby Mock Server starting...'"
}
tasks.test.dependsOn("startRubyMock")

task stopRubyMock(type: Exec) {

  executable "sh"
  args "-c", "echo '==> Ruby Mock Server is stopping...'"
}
tasks.build.dependsOn("stopRubyMock")
1 Like

The following will not start a ruby shell process:

task startRubyMock(type: Exec) {
    workingDir = "src/test/ruby/server_mock"
    executable "sh"
    args "-c", "ruby", "server_mock.rb"
}
tasks.test.dependsOn("startRubyMock")

Though this will:

task startRubyMock(type: Exec) {
    workingDir = "src/test/ruby/server_mock"
    executable "ruby"
    args "server_mock.rb"
}
tasks.test.dependsOn("startRubyMock")

However it blocks further processing of gradle, just like the exec-maven-plugin does. That is why we use process-exec-maven-plugin instead.
It will not go further and stops:

> Task :startRubyMock 
Task ':startRubyMock' is not up-to-date because:
  Task has not declared any outputs.
Starting process 'command 'ruby''. Working directory: 
/home/sverre/workspace/server-api/src/test/ruby/server_mock Command: ruby 
server_mock.rb
Successfully started process 'command 'ruby''
<=====--------> 38% EXECUTING [1m 4s]
> :startRubyMock

Last:
How do I stop the process started for startRubyMock in stopRubyMock ?

If something like this would be possible?

def proc = task startRubyMock(type: Exec) {
    executable "sh"
    args "-c", "ruby", "server_mock.rb"
}

task stopRubyMock(type: Exec) {
    executable "sh"
    args "-c", "kill", proc.getPid()
}

There is a very old plugin that seems to do what process-exec-maven-plugin does for gradle.

It hasn’t been maintained for over 4 years. So in effect it is a dead plugin.

This has an example that seems to do exactly what I want:

task startServer(type: Fork) {
     // Start some external service
}

task stopServer << {
    startServer.processHandle.abort()
}

test.dependsOn startServer
test.finalizedBy stopServer

Seems I am not the only one interested in this old plugin.
There has been activity from others 7 days ago to update this plugin to support Gradle 4.5.

You’re right. Exec blocks further processing. Could you use this plugin (I mean the second plugin)?

The plugin gradle-processes could be used, provided it is maintained. One user has forked the project and begun an upgrade to support Gradle 4.5. Will wait to see what happens on this plugin.

I am not very keen on the plugin name, nor its naming of the tasks Fork/JavaFork. Also that I have to add several extra lines in order to use this plugin to “add the plugin library to your build’s buildscript.”.

I have just used this to start a node based server (http-server) installed on the build machine, and it works fine.

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// PLUGINS/DEPENDENCIES
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
plugins {
	// Include plug-in for starting a new process
	id "com.github.johnrengelman.processes" version "0.5.0"
}

apply plugin: "com.github.johnrengelman.processes"

// Import the object for starting a new process
import com.github.jengelman.gradle.plugins.processes.tasks.Fork

// Support for building and testing
apply plugin: 'java'

// Resolve Maven dependencies as Maven does
repositories {
	mavenCentral()
	mavenLocal()
}

// Cf. Maven <dependencies>
ext.seleniumVersion = '3.0.1'
dependencies {
	compile('org.testng:testng:6.11')
	compile group: 'org.seleniumhq.selenium', name: 'selenium-firefox-driver', version:seleniumVersion
	compile group: 'org.seleniumhq.selenium', name: 'selenium-chrome-driver', version:seleniumVersion
	compile group: 'org.seleniumhq.selenium', name: 'selenium-api', version:seleniumVersion
	compile group: 'org.seleniumhq.selenium', name: 'selenium-java', version:seleniumVersion
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CONFIGURE PLUGINS
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// configure name and version for jar
jar {
	version  '1.0'
	baseName 'SeleniumStarter'
	extension '.jar'
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CUSTOM TASKS
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// Start the node http-server
task startServer(dependsOn: 'compileTestJava', type: Fork) {
	// Start some external service
	workingDir project.projectDir.toString();
	String appData = System.getenv('AppData');
	String httpServerPath = "${appData}\\npm\\node_modules\\http-server\\bin\\http-server";
	assert appData != null, "ASSERTION FAILURE: appData was not defined"
	assert file(httpServerPath).exists(), "ASSERTION FAILURE: could not fine http-server at \"${httpServerPath}\""
	// node.exe location is resolved from %PATH%
	commandLine  "node.exe", httpServerPath, "-p", "18298"
}

// Stop the node http-server
task stopServer {
	doLast {
		startServer.processHandle.abort()
	}
}

// Run the tests
task functionalTest(dependsOn: ['startServer'] , type: Test) {
	description 'Runs All Selenium Functional Tests'
	outputs.upToDateWhen {return false}
	useTestNG {
		excludeGroups 'toExclude'
		useDefaultListeners = true
	}
}
// Ensure server stopped after tests are run
functionalTest.finalizedBy stopServer