Skip to content

Change default custom validator mode from EXCLUSIVE to COMBINED #1531

Description

@bbortt

Citrus Version

5.0.0-M1 (targeting 5.0.0 / Matrix release)

Context

With the major 5.0 release underway, this is the right moment to introduce breaking changes that clean up long-standing API confusion.

The current EXCLUSIVE default for custom validators was intentional — it was designed to let users substitute the default validation logic entirely with their own. However, this default causes subtle, hard-to-diagnose issues in practice.

Problem

When a custom validator is provided, it runs exclusively, silently bypassing all other validation. This means any other constraints passed to validate() are simply ignored. Two concrete (pseudo-code'ish) examples:

Example 1 — contradictory assertions on the same field:

javatestRunner.then(
  receiveMessage()
    .validate(jsonPath().expression("$.id", "foo"))
    .validate((message, testContext) -> {
      assertThat(message).contains("$.id", "bar");
    })
);

The JSON Path assertion ($.id == "foo") is silently skipped. Only the custom validator runs.

Example 2 — custom validator makes all other configuration irrelevant:

testCaseRunner.then(
  api.receiveGetAccountDetails(NOT_FOUND)
    .validate((message, testContext) -> assertThat(message.getPayload(String.class)).isEmpty())
);

No matter what else is configured on receiveGetAccountDetails(NOT_FOUND), the test passes as long as the custom validator does — giving a false sense of security.
Both cases led to real debugging confusion (see #1422).

Suggested Fix

Change the default custom validator mode fromEXCLUSIVE to COMBINED, so that custom validators run alongside other validation rather than replacing it:

public static final CustomValidatorStrategy CUSTOM_VALIDATOR_STRATEGY_DEFAULT = CustomValidatorStrategy.EXCLUSIVE;

Users who genuinely need the old exclusive behaviour can still opt in explicitly, but the footgun default is gone.

Metadata

Metadata

Assignees

Labels

enhancementprio-highreviewIf pull-request has been opened an is ready/in review

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions