You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ws.apache.org by ve...@apache.org on 2026/02/28 09:25:24 UTC
(ws-axiom) branch master updated: Remove Axiom examples from Guice-based test suite design doc
This is an automated email from the ASF dual-hosted git repository.
veithen pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ws-axiom.git
The following commit(s) were added to refs/heads/master by this push:
new b0d45d3d4 Remove Axiom examples from Guice-based test suite design doc
b0d45d3d4 is described below
commit b0d45d3d4ca0cd075674b704a1cc6a8714ebe4f3
Author: Andreas Veithen-Knowles <an...@gmail.com>
AuthorDate: Sat Feb 28 09:25:12 2026 +0000
Remove Axiom examples from Guice-based test suite design doc
Focus the proposed new design entirely on the SAAJ test suite by
removing all Axiom-specific examples (OMTestSuiteBuilder,
SOAPTestSuiteBuilder, AxiomTestCase, SerializationStrategy, etc.)
and the multi-dimension injector hierarchy diagrams.
---
docs/design/test-suite-pattern.md | 202 ++++++--------------------------------
1 file changed, 32 insertions(+), 170 deletions(-)
diff --git a/docs/design/test-suite-pattern.md b/docs/design/test-suite-pattern.md
index eea02d7c0..09c4119e4 100644
--- a/docs/design/test-suite-pattern.md
+++ b/docs/design/test-suite-pattern.md
@@ -24,12 +24,10 @@ Reusable test suites and parameterization
The Axiom project provides reusable API test suites that can be applied to different
implementations of the same API. For example, `saaj-testsuite` defines tests for the
-SAAJ API that can be executed against any `SAAJMetaFactory`, and `axiom-testsuite`
-defines tests for the Axiom API that can be executed against any `OMMetaFactory`.
+SAAJ API that can be executed against any `SAAJMetaFactory`.
-Most test suites also execute tests across multiple dimensions. For instance, SAAJ and
-SOAP tests run against both SOAP 1.1 and SOAP 1.2, while `OMTestSuiteBuilder` multiplies
-tests across XML samples, serialization strategies, builder factories, and more.
+Test suites also execute tests across multiple dimensions. For instance, SAAJ tests
+run against both SOAP 1.1 and SOAP 1.2.
This document examines the current pattern used to implement these test suites and
evaluates whether JUnit 5's `@TestFactory` mechanism offers a better approach.
@@ -93,17 +91,6 @@ For 6 test cases × 2 SOAP versions = 12 test instances, the main files are:
| `TestExamineMustUnderstandHeaderElements.java` | Test case class |
| `SAAJRITest.java` | JUnit 3 runner for the reference implementation |
-### Usage across the project
-
-The same pattern is used at much larger scale in other modules:
-
-| Module | Builder | `addTest()` calls | Estimated runtime tests |
-|--------|---------|-------------------|------------------------|
-| axiom-testsuite | `OMTestSuiteBuilder` | ~452 | Thousands (combinatorial) |
-| axiom-testsuite | `SOAPTestSuiteBuilder` | ~197 | Hundreds |
-| dom-testsuite | `DOMTestSuiteBuilder` | Many | Hundreds |
-| saaj-testsuite | `SAAJTestSuiteBuilder` | 6 | 12 |
-
## Alternative: JUnit 5 @TestFactory + DynamicTest
JUnit 5 provides a built-in mechanism for dynamic test generation that directly
@@ -189,9 +176,9 @@ class SAAJRITests extends SAAJTests {
## Considerations for migration
-### saaj-testsuite (small suite)
+### saaj-testsuite
-For the saaj-testsuite specifically, migrating to JUnit 5 `@TestFactory` would:
+For the saaj-testsuite, migrating to JUnit 5 `@TestFactory` would:
* Collapse 6 test case classes into methods within a single class.
* Remove the need for `SAAJTestSuiteBuilder` entirely.
@@ -202,21 +189,6 @@ For the saaj-testsuite specifically, migrating to JUnit 5 `@TestFactory` would:
The `SAAJImplementation` class (which uses reflection to access protected methods on
`SAAJMetaFactory`) would be retained as-is.
-### Large suites (OMTestSuiteBuilder, SOAPTestSuiteBuilder)
-
-The larger suites present additional considerations:
-
-* The one-class-per-test pattern, while verbose, keeps each test independently
- navigable and organizes tests by the API area they cover.
-* The exclusion mechanism (LDAP filters on test parameters) is heavily used by
- consumers to skip known-failing tests for specific implementations. A JUnit 5
- equivalent would need to provide comparable functionality.
-* The sheer number of test case classes (hundreds) means migration would be a
- substantial effort.
-* A phased approach is possible: a JUnit 5 adapter that wraps `MatrixTestSuiteBuilder`
- output into `DynamicTest` instances would allow consuming modules to migrate to
- JUnit 5 runners without rewriting test case classes.
-
### Replacement for MatrixTestSuiteBuilder: MatrixTestNode tree with Guice
Since `DynamicContainer` and `DynamicTest` are `final` in JUnit 5, they cannot be
@@ -253,23 +225,21 @@ abstract class MatrixTestNode {
```java
/**
* Mirrors {@link DynamicContainer}. Represents a parameterized grouping level
- * in the test tree (e.g. a SOAP version, a serialization strategy).
+ * in the test tree (e.g. a SOAP version).
*
* <p>A {@code MatrixTestContainer} holds a list of {@link Dimension} instances
- * of a given type (e.g. all {@code SOAPSpec} values, or all
- * {@code SerializationStrategy} values). When {@link #toDynamicNodes} is called,
- * it produces one {@link DynamicContainer} per dimension instance. For each
- * dimension instance, a <em>child Guice injector</em> is created from the
- * parent injector, binding the dimension type to that specific instance. This
- * child injector is then propagated to all children, forming a hierarchy where
- * each path from root to leaf accumulates all dimension bindings.
+ * of a given type (e.g. all {@code SOAPSpec} values). When
+ * {@link #toDynamicNodes} is called, it produces one {@link DynamicContainer}
+ * per dimension instance. For each dimension instance, a <em>child Guice
+ * injector</em> is created from the parent injector, binding the dimension type
+ * to that specific instance. This child injector is then propagated to all
+ * children, forming a hierarchy where each path from root to leaf accumulates
+ * all dimension bindings.
*
- * <p>Because a {@code Dimension} may contribute multiple test parameters (for
- * example, {@code SerializeToWriter} adds both {@code serializationStrategy=Writer}
- * and {@code cache=true}), the test parameters extracted from each dimension
- * determine both the display name and the filter dictionary. The full parameter
- * dictionary for any leaf {@code MatrixTest} is the accumulation of
- * parameters from its ancestor {@code MatrixTestContainer} chain.
+ * <p>The test parameters extracted from each dimension determine both the
+ * display name and the filter dictionary. The full parameter dictionary for any
+ * leaf {@code MatrixTest} is the accumulation of parameters from its ancestor
+ * {@code MatrixTestContainer} chain.
*
* @param <D> the dimension type
*/
@@ -346,7 +316,7 @@ class MatrixTestContainer<D extends Dimension> extends MatrixTestNode {
* also supported. The injector received from the ancestor
* {@code MatrixTestContainer} chain will have bindings for all dimension
* types, plus any implementation-level bindings from the root injector
- * (e.g. {@code OMMetaFactory}, {@code SAAJImplementation}).
+ * (e.g. {@code SAAJImplementation}).
*
* <p>Once the instance is created, it is executed via {@link TestCase#runBare()},
* which invokes the full {@code setUp()} → {@code runTest()} → {@code tearDown()}
@@ -404,24 +374,6 @@ Root Injector
→ testInstance.runBare()
```
-For suites with multiple dimensions the nesting deepens:
-
-```
-Root Injector
- binds: OMMetaFactory → instance
- │
- ├─ Child Injector (SOAPSpec → SOAP11)
- │ │
- │ ├─ Child Injector (SerializationStrategy → SerializeToWriter)
- │ │ └─ MatrixTest → injector has {OMMetaFactory, SOAPSpec, SerializationStrategy}
- │ │
- │ └─ Child Injector (SerializationStrategy → SerializeToOutputStream)
- │ └─ MatrixTest → injector has {OMMetaFactory, SOAPSpec, SerializationStrategy}
- │
- └─ Child Injector (SOAPSpec → SOAP12)
- └─ ...
-```
-
#### What test case classes look like
Test case classes continue to extend `junit.framework.TestCase` (or a domain-specific
@@ -477,40 +429,6 @@ public abstract class SAAJTestCase extends TestCase {
}
```
-For the larger axiom-testsuite, intermediate classes follow the same pattern:
-
-```java
-public abstract class AxiomTestCase extends TestCase {
- @Inject protected OMMetaFactory metaFactory;
-}
-
-public abstract class SOAPTestCase extends AxiomTestCase {
- @Inject protected SOAPSpec spec;
- protected SOAPFactory soapFactory;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- soapFactory = spec.getAdapter(FactorySelector.class).getFactory(metaFactory);
- }
-}
-```
-
-Test cases with multiple dimensions simply inject all of them:
-
-```java
-public class TestSerializeWithXSITypeAttribute extends SOAPTestCase {
- @Inject private SerializationStrategy serializationStrategy;
-
- @Override
- protected void runTest() throws Throwable {
- XML xml = serializationStrategy.serialize(
- soapFactory.getDefaultSOAPEnvelope().getBody());
- // ... assertions ...
- }
-}
-```
-
Note that the existing `Dimension.addTestParameters()` mechanism is **not** used by test
case classes at all. Parameters are extracted only by `MatrixTestContainer` for display
names and filter matching. Test cases interact with dimensions purely as typed objects
@@ -519,23 +437,20 @@ obtained through injection.
#### How filtering works
Each `MatrixTestContainer` level holds a list of `Dimension` instances (e.g. all
-`SOAPSpec` values or all `SerializationStrategy` values). When `toDynamicNodes()` is
-called, each container produces one `DynamicContainer` per dimension instance, and
-parameters accumulate from the root down:
+`SOAPSpec` values). When `toDynamicNodes()` is called, each container produces one
+`DynamicContainer` per dimension instance, and parameters accumulate from the root down:
```
MatrixTestContainer(SOAPSpec.class, [SOAPSpec.SOAP11, SOAPSpec.SOAP12])
- MatrixTestContainer(SerializationStrategy.class, [SerializeToWriter, ...])
- MatrixTest(TestSerializeElement.class)
- MatrixTest(TestSerializeDocument.class)
-
-→ spec=soap11 [child injector binds SOAPSpec]
- → serializationStrategy=Writer, cache=true [child injector binds SerializationStrategy]
- → TestSerializeElement (filtered against {spec=soap11, serializationStrategy=Writer, cache=true})
- → TestSerializeDocument (filtered against {spec=soap11, serializationStrategy=Writer, cache=true})
+ MatrixTest(TestAddChildElementReification.class)
+ MatrixTest(TestGetOwnerDocument.class)
+
+→ spec=soap11 [child injector binds SOAPSpec]
+ → TestAddChildElementReification (filtered against {spec=soap11})
+ → TestGetOwnerDocument (filtered against {spec=soap11})
spec=soap12
- → serializationStrategy=Writer, cache=true
- → ...
+ → TestAddChildElementReification (filtered against {spec=soap12})
+ → TestGetOwnerDocument (filtered against {spec=soap12})
```
Consumers apply exclusions and create the root injector:
@@ -562,60 +477,7 @@ class SAAJRITests {
root.addChild(new MatrixTest(TestGetOwnerDocument.class));
List<Filter> excludes = new ArrayList<>();
- return root.toDynamicNodes(rootInjector, new Hashtable<>(), excludes);
- }
-}
-```
-
-For large suites, a builder class constructs the tree:
-
-```java
-class OMTestTreeBuilder {
- private final OMMetaFactory metaFactory;
-
- OMTestTreeBuilder(OMMetaFactory metaFactory) {
- this.metaFactory = metaFactory;
- }
-
- Injector createRootInjector() {
- return Guice.createInjector(new AbstractModule() {
- @Override
- protected void configure() {
- bind(OMMetaFactory.class).toInstance(metaFactory);
- }
- });
- }
-
- MatrixTestNode build() {
- // Top-level container groups by SOAPSpec
- MatrixTestContainer<SOAPSpec> bySpec = new MatrixTestContainer<>(
- SOAPSpec.class, Multiton.getInstances(SOAPSpec.class));
-
- // Nested container groups by SerializationStrategy
- MatrixTestContainer<SerializationStrategy> bySerialization = new MatrixTestContainer<>(
- SerializationStrategy.class,
- Multiton.getInstances(SerializationStrategy.class));
- bySerialization.addChild(new MatrixTest(TestSerializeWithXSITypeAttribute.class));
- bySerialization.addChild(new MatrixTest(TestSerializeDocument.class));
- bySpec.addChild(bySerialization);
-
- // Tests that only vary by SOAPSpec (no serialization dimension)
- bySpec.addChild(new MatrixTest(TestGetSOAPVersion.class));
-
- return bySpec;
- }
-}
-```
-
-```java
-class OMImplementationTests {
- @TestFactory
- Stream<DynamicNode> omTests() {
- OMTestTreeBuilder builder = new OMTestTreeBuilder(myMetaFactory);
- Injector rootInjector = builder.createRootInjector();
- MatrixTestNode root = builder.build();
- List<Filter> excludes = new ArrayList<>();
- excludes.add(Filter.forClass(TestSerializeDocument.class, "(spec=soap12)"));
+ excludes.add(Filter.forClass(TestGetOwnerDocument.class, "(spec=soap12)"));
return root.toDynamicNodes(rootInjector, new Hashtable<>(), excludes);
}
}
@@ -639,6 +501,6 @@ class OMImplementationTests {
`addTest()` call to manually pass all dimension values to the test constructor.
With Guice, `MatrixTest` only needs the test class; the injector supplies
everything.
-* **Test case base classes become simpler.** Intermediate classes like `SAAJTestCase`,
- `SOAPTestCase`, and `AxiomTestCase` no longer need constructor parameters or chains
- of `super(...)` calls — they simply declare `@Inject` fields.
+* **Test case base classes become simpler.** `SAAJTestCase` no longer needs
+ constructor parameters or chains of `super(...)` calls — it simply declares
+ `@Inject` fields.