ScreenshotNeo

BlogHow-to

How to Speed Up Jenkins Pipelines with Test Orchestration

Learn how to split Jenkins tests into balanced parallel groups, schedule them against agent capacity, and find out whether orchestration improves your build time.

By the ScreenshotNeo team4 October 20268 min read

To speed up a Jenkins pipeline with test orchestration, identify whether test execution is the bottleneck, divide independent tests into balanced groups, and run those groups concurrently on available Jenkins agents. The Jenkins Parallel Test Executor plugin can use prior test timings to form roughly equal subsets. Your test command must also honor the plugin’s generated exclusions, and your agents need enough executor, CPU, memory, and shared-service capacity to run the groups safely.

There is no universal speedup: the result depends on how much time tests consume, how evenly they split, setup and queue overhead, and whether parallel tests interfere with each other. Measure the test stage and full pipeline separately before and after the change.

1. Find the bottleneck before splitting tests

Record durations for checkout, compilation, test setup, test execution, and result publishing across representative builds. The plugin’s test-time calculations concern test execution; they do not account for other work such as checkout or building the test. If checkout or compilation dominates, splitting tests will not remove that part of the critical path.

Also capture the number of test workers, queue delay, test failures, and resource saturation. Preserve a baseline so you can tell whether a shorter test stage actually shortens the whole pipeline.

2. Check that the test runner supports the split

The Parallel Test Executor needs JUnit-compatible XML test results and a test command that consumes an exclusion list. Jenkins can calculate partitions, but it cannot make a runner obey them. The plugin documentation explicitly assigns the build script responsibility for honoring the exclusion file.

  1. Configure the test run to publish JUnit-compatible XML results.
  2. Choose a plugin test mode that matches the granularity your runner accepts: Java classes, parameterized cases, test cases, or qualified test cases.
  3. Make the test command read the exclusion file provided for each partition.
  4. Confirm the partitions are disjoint and their union covers the intended suite. A split that the runner ignores may run the entire suite in every branch.

Check the plugin’s documentation and compatibility information against your Jenkins installation before rollout. Do not assume compatibility from the examples alone.

3. Split tests and run branches in parallel

The following Declarative Pipeline is a structural example. Replace the illustrative split step arguments and the placeholder runner command with the syntax supported by your installed plugin version and test framework. In particular, connect each generated exclusion file to your runner, and ensure the plugin receives the prior successful build’s timing data and the current test results.

pipeline {
  agent none
  stages {
    stage('Test') {
      steps {
        script {
          // Configure splitTests with the mode and target supported by
          // your installed Parallel Test Executor plugin version.
          def chunks = splitTests(
            count: 4,
            mode: 'CLASS'
          )

          def branches = [:]
          for (int i = 0; i < chunks.size(); i++) {
            def chunk = chunks[i]
            def branchName = "test-${i + 1}"

            branches[branchName] = {
              node('test-agent') {
                checkout scm
                // Adapt this command to consume this chunk's exclusions.
                sh "./run-tests --exclude-file '${chunk.exclusionFile}'"
                junit 'build/test-results/**/*.xml'
              }
            }
          }
          parallel branches
        }
      }
    }
  }
}

This snippet shows the scheduling shape rather than a drop-in configuration: plugin step parameters and returned chunk fields depend on the installed plugin and its documented Pipeline step contract. Use the Pipeline Snippet Generator or step reference for the exact syntax. Keep the `node` allocation around the work that needs an executor, and publish results from every branch with paths that do not overwrite another branch’s output.

The plugin supports count-based and time-target partition goals. A fixed count can fit a known, stable agent pool. A time target is documented for elastic agents that can scale with the amount of work. Do not create more useful work branches than your available capacity can run; excess branches wait in the queue.

4. Set concurrency to match agent capacity

A Jenkins executor is a computational slot on a controller or agent, and a Pipeline `node` schedules work onto an available slot. The number of parallel branches that can run at once is therefore bounded by available executors. Set the branch count based on the capacity of the test agents, not just the number of partitions you can generate.

  • Estimate safe concurrent runs from CPU and memory headroom.
  • Include container limits and shared dependencies such as databases, test environments, licenses, and rate-limited services.
  • Consider other jobs sharing the same agents; a branch count that works in isolation may cause contention in a busy controller.
  • Watch queue delay. If branches spend longer waiting than they save in test work, reduce concurrency or add capacity.

More branches can increase context switching, memory pressure, fixture collisions, and queueing. Start with a modest split, validate test isolation, then adjust from observed wall time and resource use.

5. Choose failure behavior deliberately

Jenkins Declarative Pipeline supports fail-fast behavior for parallel stages. Fail-fast can stop remaining work after a branch fails, which may save agent time when the first failure is sufficient. If you need a complete set of failures for diagnosis, allow the other branches to finish and collect their results. Choose based on the team’s diagnostic needs and compute constraints, then make the behavior clear in the Pipeline configuration.

Independent tests are a prerequisite for useful parallelism. Tests that share mutable data, fixed ports, a single account, or a global environment can become flaky when run concurrently. Isolate those resources, serialize the conflicting subset, or leave it out of the parallel group until it is safe.

6. Consider Maven Surefire process parallelism

Maven projects can also run tests in parallel within a test stage using Surefire’s `forkCount`. This is process-level concurrency, whereas Jenkins branches distribute work across Pipeline tasks and potentially different agents. Combining both multiplies concurrency: four Jenkins branches with a Surefire fork count of four can create up to sixteen test processes, before accounting for other work.

The Jenkins development guide gives `forkCount` examples of `1C` and `0.45C`; these are examples, not universal settings. It notes the default is `1`. For a Maven project, a configuration can be placed in the Surefire plugin section of `pom.xml`:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-surefire-plugin</artifactId>
  <configuration>
    <forkCount>1C</forkCount>
  </configuration>
</plugin>

Here, `C` is a core-based multiplier in Surefire’s configuration. Verify the behavior against the Surefire version in your project. Begin conservatively, validate test isolation, and monitor memory and CPU before increasing process count.

7. Benchmark the change honestly

  1. Run several representative builds with the existing configuration and record test-stage and whole-pipeline wall time.
  2. Enable orchestration and keep the workload, agent class, and relevant pipeline steps comparable.
  3. Record branch count, queue delay, per-branch duration, failures, and resource saturation.
  4. Compare the slowest branch and total stage duration. The slowest branch plus scheduling and setup overhead determines when the stage can finish.
  5. Keep the change only if it improves the result that matters to your team without unacceptable reliability or capacity costs.

Do not infer a fixed percentage improvement from Jenkins documentation: it describes the mechanisms, not a benchmark for your suite. Revisit partitions as test timings change, and account for cold starts or missing history on new jobs.

8. Alternatives when Jenkins-native orchestration is not enough

Approach Best fit Questions to evaluate
Jenkins Pipeline `parallel` plus Parallel Test Executor Keep Jenkins and split work using prior test timings. Does the runner emit JUnit XML and honor exclusions? Is the plugin compatible with this Jenkins installation? Are agents available?
Maven Surefire `forkCount` Run Maven tests in multiple processes within a test stage. How much memory does each process need? Are tests isolated? What is the combined concurrency with Jenkins branches?
Buildkite Test Engine Teams considering test analytics and timing-based grouping across CI systems; its documentation says it supports Jenkins. What integration and data collection are required? Does the current plan fit? Current pricing and plan limits were not verified for this article.
CircleCI test splitting Teams evaluating CircleCI’s own test execution capabilities as part of a platform decision. Does the documented capability apply to the Cloud or Server product you use? What migration and timing-data requirements apply?

Buildkite and CircleCI capabilities are vendor-documented options, not Jenkins-native plugins. Confirm current product behavior, plan terms, and fit directly with each vendor before making a platform or purchasing decision.

Or skip the browser setup

When a pipeline needs a screenshot of a page for a visual check or build artifact, ScreenshotNeo provides a website screenshot API and MCP server. Its API accepts one GET request for a URL and returns an image or PDF. See the API documentation for parameters and response details.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

Cookie and consent banners, newsletter popups, and chat widgets are removed before capture. Bot checks, blank pages, failed loads, timeouts, and cache hits cost nothing; response headers report the page verdict and billing status. An MCP server lets AI agents use the `take_screenshot`, `get_page_info`, and `capture_pdf` tools. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000.

Sign up free for 1,000 screenshots a month, no card required.

Troubleshooting

Symptom Likely cause What to check or fix
Every branch runs the full suite The runner ignores the generated exclusion data, or the exclusion path is wrong. Log the file path and contents in each branch; confirm the command consumes it and that the plugin mode matches the runner’s test identifiers.
A split is empty or tests are missing Test discovery, result format, mode, or exclusion semantics do not match. Verify JUnit XML is published, inspect the generated groups, and confirm their union covers the intended tests without overlap.
Parallel branches queue for a long time There are more branches than available executors, or agents are shared with other workloads. Compare queue time to branch duration; reduce branch count or provide suitable agent capacity.
Tests become flaky only in parallel Tests share mutable fixtures or constrained resources. Isolate data and services, allocate unique ports or accounts, or serialize conflicting tests.
The test stage improves but the pipeline does not Checkout, compilation, setup, or publishing dominates total time. Measure each stage separately and optimize the largest remaining portion of the pipeline.
Agent memory use or failures increase Pipeline branches and test processes multiply concurrency beyond capacity. Lower branch count or Surefire `forkCount`; review per-process memory and container limits.
Partitions are badly balanced Timing history is stale, incomplete, or not representative of the current suite. Ensure successful builds publish results and provide useful timing history; compare per-branch durations and revisit the split target.

FAQ

Does the plugin make any test command parallel automatically?

No. The build script must pass the generated exclusion information to a compatible runner, and the tests must produce the expected result data.

Should I use a fixed number of groups or a time target?

Use a fixed count when capacity is stable and known. A time target is documented for elastic agents. In either case, validate queueing and the slowest branch on your infrastructure.

Can I use Pipeline parallelism and Surefire forks together?

Yes, but their concurrency multiplies. Calculate the combined process load and test shared-state safety before enabling both.

Will parallelization always reduce the whole build time?

No. It helps only when independent test work is on the critical path and the system has capacity to run it concurrently. Measure the full pipeline to confirm the effect.