General Problem: I'm testing a web application at a large company with a service oriented architecture. External services often fail in our test environment due to background noise. This prevents integration tests for our service from running properly since our service won't work unless calls to these external services are succeeding. For this reason we'd like the ability to mock responses from external services so that we don't have to depend on them and can test our own service in isolation.
There's a tool for this called Mockey which we are hoping to use. It's a Java program that runs through an embedded Jetty server and acts as a proxy for service calls. Our web application is re-configured to call Mockey instead of the external services. Mockey is then configured to provide dynamically mocked responses to these calls depending on the URL and header data that get passed in.
In order to utilize this tool we'd like the ability to start Mockey during the pre-integration-test phase of a Maven lifecycle so that it will be available for use during the integration-test phase.
Specific Problem: In order to start and shutdown Mockey during the pre-integration-test and post-integration-test phases of the Maven lifecycle I've written a Maven 3 plugin called mockey-maven-plugin:
The mockey-maven-plugin pom.xml file:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.mockey</groupId>
<artifactId>mockey-maven-plugin</artifactId>
<packaging>maven-plugin</packaging>
<version>1.3</version>
<dependencies>
<!-- Maven plugin dependencies -->
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
<version>3.2.5</version>
</dependency>
<dependency>
<groupId>org.apache.maven.plugin-tools</groupId>
<artifactId>maven-plugin-annotations</artifactId>
<version>3.4</version>
<scope>provided</scope>
</dependency>
<!-- Mockey dependency -->
<dependency>
<groupId>com.mycompany.mockey</groupId>
<artifactId>Mockey</artifactId>
<version>1.16.2015</version>
</dependency>
</dependencies>
<build>
<plugins>
<!-- This plugin is used to generate a plugin descriptor
xml file which will be packaged with the plugin -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-plugin-plugin</artifactId>
<version>3.4</version>
</plugin>
</plugins>
</build>
</project>
The mockey-maven-plugin StartMockey class:
@Mojo(name="start-mockey")
@Execute(phase= LifecyclePhase.PACKAGE) // Not sure about this annotation
public class StartMockey extends AbstractMojo
{
/**
* Flag which controls Mockey startup.
*/
@Parameter(property="mockey.skipStartup", defaultValue="false", required=true)
private Boolean skipStartup;
// Do I need these getters and setters or does Maven ignore them?
public Boolean getSkipStartup()
{
return skipStartup;
}
public void setSkipStartup(Boolean skipStartup)
{
this.skipStartup = skipStartup;
}
// *SNIP* Defining Mockey parameters...
// Maven will call this method to start the mockey-maven-plugin
public void execute()
{
if(skipStartup)
{
getLog().info("Skipping Mockey startup");
return;
}
getLog().info("Starting Mockey");
// Load specified parameters into array
List<String> argsList = new ArrayList<>();
// *SNIP* Adding Mockey parameters to argList...
String[] args = new String[argsList.size()];
argsList.toArray(args);
// Start Mockey with specified parameters and wait for it to return
try
{
JettyRunner.main(args);
}
catch(Exception e)
{
getLog().error("Mockey died... :(");
}
getLog().info("mockey-maven-plugin now exiting");
}
}
The mockey-maven-plugin ShutdownMockey class:
@Mojo(name="shutdown-mockey")
public class ShutdownMockey extends AbstractMojo
{
/**
* Flag which controls Mockey shutdown.
*/
@Parameter(property="mockey.skipShutdown")
private Boolean skipShutdown;
// Again, Do I need these getters and setters or does Maven ignore them?
public Boolean getSkipShutdown()
{
return skipShutdown;
}
public void setSkipShutdown(Boolean skipShutdown)
{
this.skipShutdown = skipShutdown;
}
public void execute()
{
if(skipShutdown)
{
getLog().info("Skipping Mockey shutdown");
return;
}
getLog().info("Shutting down Mockey");
JettyRunner.stopServer();
getLog().info("mockey-maven-plugin now exiting");
}
}
Plugin entry for mockey-maven-plugin in my team project's pom.xml file:
<plugin>
<groupId>com.mycompany.mockey</groupId>
<artifactId>mockey-maven-plugin</artifactId>
<version>1.3</version>
<configuration>
<skipShutdown>${keepMockeyRunning}</skipShutdown>
<skipStartup>${skipMockey}</skipStartup>
<!-- *SNIP* Other Mockey parameters... -->
</configuration>
<executions>
<execution>
<id>start-mockey</id>
<goals>
<goal>start-mockey</goal>
</goals>
<phase>pre-integration-test</phase>
</execution>
<execution>
<id>shutdown-mockey</id>
<goals>
<goal>shutdown-mockey</goal>
</goals>
<phase>post-integration-test</phase>
</execution>
</executions>
</plugin>
This plugin works fine for starting Mockey in the pre-integration-test phase, but blocks the build until Mockey has exited. I'm not sure why this is occurring since I added this annotation specifically to prevent that issue:
@Execute(phase= LifecyclePhase.PACKAGE)
I actually copied this annotation from another plugin which does exactly what I'm trying to do here (We use the maven-tomcat7-plugin for launching our web application locally in the pre-integration-test phase and shutting it down in the post-integration-test phase). I thought this would work the same way, but I'm seeing a different behavior.
Here's what I want to see happen:
Here's what I'm actually seeing happen:
This confuses me primarily because Maven seems to have a different concept of forking than what I'm familiar with. To me forking means to diverge at a particular point, not to start over, and not to affect the original process. When we fork a process in Unix it copies the stack and function pointer of the first process. It does not start over from the beginning of the program. Similarly, when we fork a code repository we start with all of the files and directories that are currently in the original repository. We don't start over with a blank slate. So why, when we "fork" a Maven lifecycle does it abandon everything, start over, and block the original thread? That doesn't seem at all like forking to me. Here's some of the documentation I've read that describes "forking" in Maven:
Remaining Questions:
Answered Question (see below): - Is there another phase which I should specify in the annotation for my plugin to get it to behave as desired or should I be using the execute annotation in a fundamentally different manner?
See https://books.sonatype.com/mvnref-book/reference/writing-plugins-sect-plugins-lifecycle.html
The docs seem to indicate that you should create a custom lifecycle that only includes the start-mockey goal. Then your @Execute annotation should specify the goal and the lifecycle. That should fork off the execution but only execute your start-mockey. I think then you can run the end-mockey as normal.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With