Class RealJenkinsExtension
- All Implemented Interfaces:
org.junit.jupiter.api.extension.AfterEachCallback,org.junit.jupiter.api.extension.BeforeEachCallback,org.junit.jupiter.api.extension.Extension
JenkinsSessionExtension but running Jenkins in a more realistic environment.
Though Jenkins is run in a separate JVM using Winstone (java -jar jenkins.war),
you can still do “whitebox” testing: directly calling Java API methods, starting from JenkinsRule or not.
This is because the test code gets sent to the remote JVM and loaded and run there.
(Thus when using Maven, there are at least three JVMs involved:
Maven itself; the Surefire booter with your top-level test code; and the Jenkins controller with test bodies.)
Just as with JenkinsRule, all plugins found in the test classpath will be enabled,
but with more realistic behavior: class loaders in a graph, pluginFirstClassLoader and maskClasses, etc.
“Compile-on-save” style development works for classes and resources in the current plugin:
with a suitable IDE, you can edit a source file, have it be sent to target/classes/,
and rerun a test without needing to go through a full Maven build cycle.
This is because target/test-classes/the.hpl is used to load unpacked plugin resources.
Like JenkinsRule, the controller is started in “development mode”:
the setup wizard is suppressed, the update center is not checked, etc.
Known limitations:
- Execution is a bit slower due to the overhead of launching a new JVM; and class loading overhead cannot be shared between test cases. More memory is needed.
- Remote calls must be serializable. Use methods like
runRemotely(RealJenkinsExtension.StepWithReturnAndOneArg, Serializable)and/orXStreamSerializableas needed. staticstate cannot be shared between the top-level test code and test bodies (though the compiler will not catch this mistake).- When using a snapshot dep on Jenkins core, you must build
jenkins.warto test core changes (there is no “compile-on-save” support for this). TestExtensionis not available (but tryaddSyntheticPlugin(org.jvnet.hudson.test.junit.jupiter.RealJenkinsExtension.SyntheticPlugin)).LoggerRuleis not available, however additional loggers can be configured viawithLogger(Class, Level)}.BuildWatcheris not available, but you can useTailLoginstead.
Systems not yet tested:
- Possibly
Timeoutcan be used.
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic final classstatic final classstatic final classstatic classstatic final classstatic interfaceOne step to run.static interfaceRealJenkinsExtension.Step2<T extends Serializable>static classstatic interfaceRealJenkinsExtension.StepWithFourArgs<A1 extends Serializable,A2 extends Serializable, A3 extends Serializable, A4 extends Serializable> static interfacestatic interfaceRealJenkinsExtension.StepWithReturnAndFourArgs<R extends Serializable,A1 extends Serializable, A2 extends Serializable, A3 extends Serializable, A4 extends Serializable> static interfaceRealJenkinsExtension.StepWithReturnAndOneArg<R extends Serializable,A1 extends Serializable> static interfaceRealJenkinsExtension.StepWithReturnAndThreeArgs<R extends Serializable,A1 extends Serializable, A2 extends Serializable, A3 extends Serializable> static interfaceRealJenkinsExtension.StepWithReturnAndTwoArgs<R extends Serializable,A1 extends Serializable, A2 extends Serializable> static interfaceRealJenkinsExtension.StepWithThreeArgs<A1 extends Serializable,A2 extends Serializable, A3 extends Serializable> static interfaceRealJenkinsExtension.StepWithTwoArgs<A1 extends Serializable,A2 extends Serializable> static final classAlternative toaddPlugins(java.lang.String...)orTestExtensionthat lets you build a test-only plugin on the fly. -
Constructor Summary
ConstructorsConstructorDescriptionLinks this extension to another, withgetHome()to be initialized by whichever copy starts first. -
Method Summary
Modifier and TypeMethodDescriptionaddPlugins(String... plugins) Add some plugins to the test classpath.Adds a test-only plugin to the controller based on sources defined in this module.voidafterEach(org.junit.jupiter.api.extension.ExtensionContext context) voidbeforeEach(org.junit.jupiter.api.extension.ExtensionContext context) Builds aSSLContexttrusting the current instance.<T extends Serializable>
TRun a step with a return value on the remote system.static StringcheckResult(HttpURLConnection conn) Creates a test-only plugin based on sources defined in this module, but does not install it.createTempDirectory(String prefix) Creates a temporary directory.org.htmlunit.WebClientCreate a client configured to trust any self-signed certificate used by this instance.voidDeletesJENKINS_HOME.Set an extra environment variable.getHome()Obtains the Jenkins home directory.getName()Returns the autogenerated self-signed root CA in PEM format, or null ifhttps()has not been called.String[]getUrl()Similar toJenkinsRule.getURL().https()Sets up HTTPS for the current instance, and disables plain HTTP.https(String host, KeyStoreManager keyStoreManager, X509Certificate rootCA) Sets up HTTPS for the current instance, and disables plain HTTP.includeTestClasspathPlugins(boolean includeTestClasspathPlugins) The intended use case for this is to use the plugins bundled into the warwithWar(File)instead of the plugins in the pom.booleanisAlive()Returns true if the Jenkins process is alive.javaOptions(String... options) Add some JVM startup options.jenkinsOptions(String... options) Add some Jenkins (including Winstone) startup options.omitPlugins(String... plugins) Omit some plugins in the test classpath.prepareHomeLazily(boolean prepareHomeLazily) AllowsJENKINS_HOMEinitialization to be delayed untilstartJenkins()is called for the first time.voidrun(org.junit.rules.ErrorCollector errors, RealJenkinsExtension.Step step) Run a step on the remote system, but do not immediately fail, just record any error.voidrun(RealJenkinsExtension.Step step) Run a step on the remote system.voidrunRemotely(RealJenkinsExtension.Step... steps) Runs one or more steps on the remote system.<T extends Serializable>
T<A1 extends Serializable,A2 extends Serializable, A3 extends Serializable, A4 extends Serializable>
voidrunRemotely(RealJenkinsExtension.StepWithFourArgs<A1, A2, A3, A4> s, A1 arg1, A2 arg2, A3 arg3, A4 arg4) <A1 extends Serializable>
voidrunRemotely(RealJenkinsExtension.StepWithOneArg<A1> s, A1 arg1) <R extends Serializable,A1 extends Serializable, A2 extends Serializable, A3 extends Serializable, A4 extends Serializable>
RrunRemotely(RealJenkinsExtension.StepWithReturnAndFourArgs<R, A1, A2, A3, A4> s, A1 arg1, A2 arg2, A3 arg3, A4 arg4) <R extends Serializable,A1 extends Serializable>
RrunRemotely(RealJenkinsExtension.StepWithReturnAndOneArg<R, A1> s, A1 arg1) <R extends Serializable,A1 extends Serializable, A2 extends Serializable, A3 extends Serializable>
RrunRemotely(RealJenkinsExtension.StepWithReturnAndThreeArgs<R, A1, A2, A3> s, A1 arg1, A2 arg2, A3 arg3) <R extends Serializable,A1 extends Serializable, A2 extends Serializable>
RrunRemotely(RealJenkinsExtension.StepWithReturnAndTwoArgs<R, A1, A2> s, A1 arg1, A2 arg2) <A1 extends Serializable,A2 extends Serializable, A3 extends Serializable>
voidrunRemotely(RealJenkinsExtension.StepWithThreeArgs<A1, A2, A3> s, A1 arg1, A2 arg2, A3 arg3) <A1 extends Serializable,A2 extends Serializable>
voidrunRemotely(RealJenkinsExtension.StepWithTwoArgs<A1, A2> s, A1 arg1, A2 arg2) voidSwitch the Jenkins home directory.voidvoidStops Jenkins and releases any system resources associated with it.voidStops Jenkins abruptly, without giving it a chance to shut down cleanly.voidthen(RealJenkinsExtension.Step... steps) Run one Jenkins session, send one or more test thunks, and shut down.<T extends Serializable>
TRun one Jenkins session, send a test thunk, and shut down.withBootClasspath(File... files) Applies ANSI coloration to log lines produced by this instance, complementingwithName(java.lang.String).withDebugPort(int debugPort) Allows usage of a static debug port instead of a random one.withDebugServer(boolean debugServer) Allows to use debug in server mode or client mode.withDebugSuspend(boolean debugSuspend) Whether to suspend the controller VM on startup until debugger is connected.UsewithFIPSEnabled(FIPSTestBundleProvider)with default value ofFIPSTestBundleProvider.get()withFIPSEnabled(io.jenkins.test.fips.FIPSTestBundleProvider fipsTestBundleProvider) Sets a custom host name for the Jenkins root URL.withHttpListenAddress(String httpListenAddress) Provides a custom interface to listen to.withJavaHome(String JavaHome) Allows to specify a java home, defaults to JAVA_HOME if not usedwithLogger(Class<?> clazz, Level level) withLogger(String logger, Level level) Sets a name for this instance, which will be prefixed to log messages to simplify debugging.withPackageLogger(Class<?> clazz, Level level) withPort(int port) Provides a custom fixed port instead of a random one.withPrefix(String prefix) Sets a custom prefix for the Jenkins root URL.withTimeout(int timeout) Adjusts the test timeout.Sets a custom WAR file to be used by the extension instead of the one in the path orwar/target/jenkins.warin case of core.
-
Constructor Details
-
RealJenkinsExtension
public RealJenkinsExtension() -
RealJenkinsExtension
Links this extension to another, withgetHome()to be initialized by whichever copy starts first. Also copies configuration related to the setup of that directory:includeTestClasspathPlugins(boolean),addPlugins(java.lang.String...),addSyntheticPlugin(org.jvnet.hudson.test.junit.jupiter.RealJenkinsExtension.SyntheticPlugin), andomitPlugins(java.lang.String...). Other configuration such asjavaOptions(String...)may be applied to both, but that is your choice.
-
-
Method Details
-
addPlugins
Add some plugins to the test classpath.- Parameters:
plugins- Filenames of the plugins to install. These are expected to be absolute test classpath resources, such asplugins/workflow-job.hpifor example.For small fake plugins built for this purpose and exercising some bit of code, use
addSyntheticPlugin(org.jvnet.hudson.test.junit.jupiter.RealJenkinsExtension.SyntheticPlugin). If you wish to test with larger archives of real plugins, this is possible for example by bindingdependency:copyto theprocess-test-resourcesphase.In most cases you do not need this method. Simply add whatever plugins you are interested in testing against to your POM in
testscope. These, and their transitive dependencies, will be loaded in allRealJenkinsExtensiontests. This method is useful if only a particular test may load the tested plugin, or if the tested plugin is not available in a repository for use as a test dependency.
-
addSyntheticPlugin
Adds a test-only plugin to the controller based on sources defined in this module. Useful when you wish to define some types, register someExtensions, etc. and there is no existing plugin that does quite what you want (that you are comfortable adding to the test classpath and maintaining the version of).If you also have some test suites based on
JenkinsRule, you may not want to useExtensionsince (unlikeTestExtension) it would be loaded in all such tests. Instead create apackage-info.javaspecifying an@OptionalPackagewhoserequirePluginslists the sameRealJenkinsExtension.SyntheticPlugin.shortName(String). (You will need to.header("Plugin-Dependencies", "variant:0")to use this API.) Then use@OptionalExtensionon all your test extensions. These will then be loaded only inRealJenkinsExtension-based tests requesting this plugin.- Parameters:
plugin- the configuredRealJenkinsExtension.SyntheticPlugin
-
createSyntheticPlugin
public File createSyntheticPlugin(RealJenkinsExtension.SyntheticPlugin plugin) throws IOException, URISyntaxException Creates a test-only plugin based on sources defined in this module, but does not install it.See
addSyntheticPlugin(org.jvnet.hudson.test.junit.jupiter.RealJenkinsExtension.SyntheticPlugin)for more details. Prefer that method if you simply want the plugin to be installed automatically.- Parameters:
plugin- the configuredRealJenkinsExtension.SyntheticPlugin- Returns:
- the JPI file for the plugin
- Throws:
IOExceptionURISyntaxException- See Also:
-
omitPlugins
Omit some plugins in the test classpath.- Parameters:
plugins- one or more code names, liketoken-macro
-
javaOptions
Add some JVM startup options.- Parameters:
options- one or more options, like-Dorg.jenkinsci.Something.FLAG=true
-
jenkinsOptions
Add some Jenkins (including Winstone) startup options. You probably meant to usejavaOptions(String...).- Parameters:
options- one or more options, like--webroot=/tmp/war --pluginroot=/tmp/plugins
-
extraEnv
Set an extra environment variable.- Parameters:
value- null to cancel a previously set variable
-
withTimeout
Adjusts the test timeout. The timer starts whenstartJenkins()completes andrunRemotely(org.jvnet.hudson.test.junit.jupiter.RealJenkinsExtension.Step...)is ready. The default is currently set to 600 (10m).- Parameters:
timeout- number of seconds before exiting, or zero to disable
-
withHost
Sets a custom host name for the Jenkins root URL.By default, this is just
localhost. But you may wish to set it to something else that resolves to localhost, such assome-id.localtest.me. This is particularly useful when running multiple copies of Jenkins (and/or other services) in one test case, since browser cookies are sensitive to host but not port and so otherwiseHttpServletRequest.getSession(boolean)might accidentally be shared across otherwise distinct services.Calling this method does not change the fact that Jenkins will be configured to listen only on localhost for security reasons (so others in the same network cannot access your system under test, especially if it lacks authentication).
When using HTTPS, use
https(String, KeyStoreManager, X509Certificate)instead. -
withPrefix
Sets a custom prefix for the Jenkins root URL.By default, the prefix defaults to
/jenkins.If not empty, must start with '/' and not end with '/'.
-
withWar
Sets a custom WAR file to be used by the extension instead of the one in the path orwar/target/jenkins.warin case of core. -
withJavaHome
Allows to specify a java home, defaults to JAVA_HOME if not used -
withLogger
-
withPackageLogger
-
withLogger
-
withName
Sets a name for this instance, which will be prefixed to log messages to simplify debugging. -
getName
-
withColor
Applies ANSI coloration to log lines produced by this instance, complementingwithName(java.lang.String). Ignored when on CI. -
withPort
Provides a custom fixed port instead of a random one.- Parameters:
port- a custom port to use instead of a random one.
-
withHttpListenAddress
Provides a custom interface to listen to.Important: for security reasons this should be overridden only in special scenarios, such as testing inside a Docker container. Otherwise a developer running tests could inadvertently expose a Jenkins service without password protection, allowing remote code execution.
- Parameters:
httpListenAddress- network interface such as0.0.0.0
. Defaults to127.0.0.1
.
-
withDebugPort
Allows usage of a static debug port instead of a random one.This allows to use predefined debug configurations in the IDE.
Typical usage is in a base test class where multiple named controller instances are defined with fixed ports
public RealJenkinsExtension cc1 = new RealJenkinsExtension().withName("cc1").withDebugPort(4001).withDebugServer(false); public RealJenkinsExtension cc2 = new RealJenkinsExtension().withName("cc2").withDebugPort(4002).withDebugServer(false);Then have debug configurations in the IDE set for ports
- 5005 (test VM) - debugger mode "attach to remote vm"
- 4001 (cc1) - debugger mode "listen to remote vm"
- 4002 (cc2) - debugger mode "listen to remote vm"
This allows for debugger to reconnect in scenarios where restarts of controllers are involved.
- Parameters:
debugPort- the TCP port to use for debugging this Jenkins instance. Between 0 (random) and 65536 (excluded).
-
withDebugServer
Allows to use debug in server mode or client mode. Client mode is friendlier to controller restarts.- Parameters:
debugServer- true to use server=y, false to use server=n- See Also:
-
withDebugSuspend
Whether to suspend the controller VM on startup until debugger is connected. Defaults to false.- Parameters:
debugSuspend- true to suspend the controller VM on startup until debugger is connected.
-
includeTestClasspathPlugins
The intended use case for this is to use the plugins bundled into the warwithWar(File)instead of the plugins in the pom. A typical scenario for this feature is a test which does not live inside a plugin's src/test/java- Parameters:
includeTestClasspathPlugins- false if plugins from pom should not be used (default true)
-
prepareHomeLazily
AllowsJENKINS_HOMEinitialization to be delayed untilstartJenkins()is called for the first time.This allows methods such as
addPlugins(java.lang.String...)to be called dynamically inside of test methods, which enables related tests that need to configureRealJenkinsExtensionin different ways to be defined in the same class using only a single instance ofRealJenkinsExtension. -
withFIPSEnabled
UsewithFIPSEnabled(FIPSTestBundleProvider)with default value ofFIPSTestBundleProvider.get() -
withFIPSEnabled
public RealJenkinsExtension withFIPSEnabled(io.jenkins.test.fips.FIPSTestBundleProvider fipsTestBundleProvider) - Parameters:
fipsTestBundleProvider- theFIPSTestBundleProviderto use for testing
-
withBootClasspath
- Parameters:
files- add someFileto bootclasspath
-
getJacocoAgentOptions
-
beforeEach
- Specified by:
beforeEachin interfaceorg.junit.jupiter.api.extension.BeforeEachCallback- Throws:
Exception
-
afterEach
- Specified by:
afterEachin interfaceorg.junit.jupiter.api.extension.AfterEachCallback- Throws:
Exception
-
deprovision
DeletesJENKINS_HOME.This method does not need to be invoked when using
@Ruleor@ClassRuleto runRealJenkinsRule.- Throws:
Exception
-
createTempDirectory
Creates a temporary directory. UnlikeFiles.createTempDirectory(String, FileAttribute...)this will be cleaned up after the test exits (likeTemporaryFolder), and will honorjava.io.tmpdirset by Surefire afterStaticProperty.JAVA_IO_TMPDIRhas been initialized.- Throws:
IOException
-
isAlive
public boolean isAlive()Returns true if the Jenkins process is alive. -
getTruststoreJavaOptions
-
then
Run one Jenkins session, send one or more test thunks, and shut down.- Throws:
Throwable
-
then
Run one Jenkins session, send a test thunk, and shut down.- Throws:
Throwable
-
getUrl
Similar toJenkinsRule.getURL(). Requires Jenkins to be started before usingstartJenkins().Always ends with a '/'.
- Throws:
MalformedURLException
-
https
Sets up HTTPS for the current instance, and disables plain HTTP. This generates a self-signed certificate for localhost. The corresponding root CA that needs to be trusted by HTTP client can be obtained usinggetRootCA().- Returns:
- the current instance
- See Also:
-
https
public RealJenkinsExtension https(@NonNull String host, @NonNull KeyStoreManager keyStoreManager, @NonNull X509Certificate rootCA) Sets up HTTPS for the current instance, and disables plain HTTP.You don't need to call
withHost(String)when calling this method.- Parameters:
host- the host name to use in the certificatekeyStoreManager- a key store manager containing the key and certificate to use for HTTPS. It needs to be valid for the given hostrootCA- the certificate that needs to be trusted by callers.- Returns:
- the current instance
- See Also:
-
getRootCA
- Returns:
- the current autogenerated root CA or null if
https()has not been called.
-
getRootCAPem
Returns the autogenerated self-signed root CA in PEM format, or null ifhttps()has not been called. Typically used to configureInboundAgentRule.Options.Builder.cert(java.lang.String).- Returns:
- the root CA in PEM format, or null if unavailable
-
buildSSLContext
Builds aSSLContexttrusting the current instance.- Throws:
NoSuchAlgorithmException
-
getHome
Obtains the Jenkins home directory. Normally it will suffice to useLocalDatato populate files. -
setHome
Switch the Jenkins home directory. Will affect subsequent startups of this extension, but not other copies linked viaRealJenkinsExtension(RealJenkinsExtension). Normally unnecessary but could be used to simulate running on the wrong home. -
createWebClient
public org.htmlunit.WebClient createWebClient()Create a client configured to trust any self-signed certificate used by this instance. -
startJenkins
- Throws:
Exception
-
checkResult
- Throws:
IOException
-
stopJenkins
Stops Jenkins and releases any system resources associated with it. If Jenkins is already stopped then invoking this method has no effect.- Throws:
Exception
-
stopJenkinsForcibly
public void stopJenkinsForcibly()Stops Jenkins abruptly, without giving it a chance to shut down cleanly. If Jenkins is already stopped then invoking this method has no effect. -
runRemotely
Runs one or more steps on the remote system. (Compared to multiple calls, passing a series of steps is slightly more efficient as only one network call is made.)- Throws:
Throwable
-
run
Run a step on the remote system. Alias forrunRemotely(RealJenkinsExtension.Step...)(with one step) that is easier to resolve for lambdas.- Throws:
Throwable
-
run
Run a step on the remote system, but do not immediately fail, just record any error. Same asErrorCollector.checkSucceeds(java.util.concurrent.Callable<T>)but more concise to call. -
runRemotely
- Throws:
Throwable
-
call
Run a step with a return value on the remote system. Alias forrunRemotely(RealJenkinsExtension.Step2)that is easier to resolve for lambdas.- Throws:
Throwable
-
runRemotely
public <A1 extends Serializable> void runRemotely(RealJenkinsExtension.StepWithOneArg<A1> s, A1 arg1) throws Throwable - Throws:
Throwable
-
runRemotely
public <A1 extends Serializable,A2 extends Serializable> void runRemotely(RealJenkinsExtension.StepWithTwoArgs<A1, A2> s, A1 arg1, A2 arg2) throws Throwable- Throws:
Throwable
-
runRemotely
public <A1 extends Serializable,A2 extends Serializable, void runRemotelyA3 extends Serializable> (RealJenkinsExtension.StepWithThreeArgs<A1, A2, throws ThrowableA3> s, A1 arg1, A2 arg2, A3 arg3) - Throws:
Throwable
-
runRemotely
public <A1 extends Serializable,A2 extends Serializable, void runRemotelyA3 extends Serializable, A4 extends Serializable> (RealJenkinsExtension.StepWithFourArgs<A1, A2, throws ThrowableA3, A4> s, A1 arg1, A2 arg2, A3 arg3, A4 arg4) - Throws:
Throwable
-
runRemotely
public <R extends Serializable,A1 extends Serializable> R runRemotely(RealJenkinsExtension.StepWithReturnAndOneArg<R, A1> s, A1 arg1) throws Throwable- Throws:
Throwable
-
runRemotely
public <R extends Serializable,A1 extends Serializable, R runRemotelyA2 extends Serializable> (RealJenkinsExtension.StepWithReturnAndTwoArgs<R, A1, throws ThrowableA2> s, A1 arg1, A2 arg2) - Throws:
Throwable
-
runRemotely
public <R extends Serializable,A1 extends Serializable, R runRemotelyA2 extends Serializable, A3 extends Serializable> (RealJenkinsExtension.StepWithReturnAndThreeArgs<R, A1, throws ThrowableA2, A3> s, A1 arg1, A2 arg2, A3 arg3) - Throws:
Throwable
-
runRemotely
public <R extends Serializable,A1 extends Serializable, R runRemotelyA2 extends Serializable, A3 extends Serializable, A4 extends Serializable> (RealJenkinsExtension.StepWithReturnAndFourArgs<R, A1, throws ThrowableA2, A3, A4> s, A1 arg1, A2 arg2, A3 arg3, A4 arg4) - Throws:
Throwable
-