ConfigServiceRepository leverages brXM's production ConfigurationConfigService
to create HST configuration in tests, providing production-identical JCR structure without manual node construction.
Key Benefits:
With BRUT 5.1.0+, ConfigServiceRepository integration is a one-liner via the loadProjectContent attribute:
@BrxmJaxrsTest(
beanPackages = {"org.example.model"},
resources = {MyResource.class},
loadProjectContent = true // Enables ConfigServiceRepository
)
class MyTest {
@Test
void testEndpoint(DynamicJaxrsTest brxm) {
brxm.request()
.get("/site/api/my-endpoint")
.assertStatus(200);
}
}
What loadProjectContent = true does:
pom.xml groupIdtarget/test-classes/META-INF/hcm-module.yamlYou still need the HCM module descriptor and config files (see Manual Setup below), but the annotation handles the repository wiring automatically.
BRUT automatically discovers addon HCM modules from the test classpath. Any JAR that provides
META-INF/hcm-module.yaml and is not a Hippo platform module (group prefix
hippo / onehippo) and not your project's own module is loaded
automatically — before your project modules so its node types are registered first.
No annotation parameter is needed — addon modules are discovered and loaded automatically:
// brxm-discovery-cms (or any other addon JAR) is auto-discovered from the classpath
@BrxmPageModelTest
class MyPageModelTest {
}
Use excludeDependencyHcmModules to skip a module that causes conflicts:
@BrxmPageModelTest(excludeDependencyHcmModules = {"some-conflicting-module"})
class MyPageModelTest {
}
Use dependencyHcmModules to explicitly include a module that auto-discovery
would miss (e.g., its group name matches a platform prefix):
@BrxmPageModelTest(dependencyHcmModules = {"some-special-module"})
class MyPageModelTest {
}
A classpath JAR is treated as an addon and loaded automatically when all of the following are true:
META-INF/hcm-module.yamlhippo or onehippo (platform modules)excludeDependencyHcmModulesFor legacy abstract class tests or advanced customization, use manual Spring XML configuration:
File: src/test/resources/META-INF/hcm-module.yaml
group:
name: myproject-test
project: myproject-test
module:
name: test-config
Important: Do NOT include config: or after: sections.
ModuleReader discovers config by directory convention.
Directory: src/test/resources/hcm-config/hst/
File: demo-hst.yaml
definitions:
config:
/hst:myproject:
jcr:primaryType: hst:hst
/hst:myproject/hst:sites:
jcr:primaryType: hst:sites
/hst:myproject/hst:sites/myproject:
jcr:primaryType: hst:site
hst:content: /content/documents/myproject
/hst:myproject/hst:configurations:
jcr:primaryType: hst:configurations
/hst:myproject/hst:configurations/myproject:
jcr:primaryType: hst:configuration
/hst:myproject/hst:configurations/myproject/hst:sitemap:
jcr:primaryType: hst:sitemap
/hst:myproject/hst:configurations/myproject/hst:sitemap/root:
jcr:primaryType: hst:sitemapitem
hst:componentconfigurationid: hst:pages/homepage
/hst:myproject/hst:configurations/myproject/hst:pages:
jcr:primaryType: hst:pages
/hst:myproject/hst:configurations/myproject/hst:pages/homepage:
jcr:primaryType: hst:component
/hst:myproject/hst:hosts:
jcr:primaryType: hst:virtualhosts
Note: BRUT uses /hst:myproject as HST root (not /hst:hst) for test isolation.
File: src/test/resources/org/example/config-service-jcr.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.1.xsd">
<bean id="javax.jcr.Repository"
class="org.bloomreach.forge.brut.resources.ConfigServiceRepository"
init-method="init"
destroy-method="close">
<constructor-arg ref="cndResourcesPatterns"/>
<constructor-arg ref="contributedCndResourcesPatterns"/>
<constructor-arg ref="yamlResourcesPatterns"/>
<constructor-arg ref="contributedYamlResourcesPatterns"/>
<constructor-arg value="myproject"/> <!-- project namespace -->
</bean>
</beans>
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class MyIntegrationTest extends AbstractJaxrsTest {
@Override
protected List<String> contributeSpringConfigurationLocations() {
return Arrays.asList(
"/org/example/config-service-jcr.xml", // ConfigServiceRepository override
"/org/example/custom-jaxrs.xml",
"/org/example/rest-resources.xml"
);
}
@Override
protected String contributeHstConfigurationRootPath() {
return "/hst:myproject"; // BRUT uses project-specific root
}
@Test
void testHstStructureCreated() throws Exception {
Repository repo = getComponentManager().getComponent(Repository.class);
Session session = repo.login(new SimpleCredentials("admin", "admin".toCharArray()));
assertTrue(session.nodeExists("/hst:myproject"));
assertTrue(session.nodeExists("/hst:myproject/hst:configurations/myproject"));
}
}
ConfigServiceRepository uses ModuleReader for explicit module loading:
Test Resources (target/test-classes)
├── META-INF/hcm-module.yaml <- Module descriptor
└── hcm-config/ <- Config discovered by convention
└── hst/*.yaml <- Your HST definitions
|
ModuleReader <- Loads module explicitly (no classpath scan)
|
ConfigurationModelImpl.build() <- Builds configuration model
|
ConfigurationConfigService <- brXM's production bootstrap service
|
JCR Repository <- Production-identical structure
Key Insight: We use ModuleReader.read(path, false) to load project test
modules explicitly by path, and scan the classpath for addon modules (non-platform, non-project JARs with
META-INF/hcm-module.yaml), avoiding ClasspathConfigurationModelReader which
would include framework JARs with unmet dependencies.
META-INF/hcm-module.yaml in target/test-classeshcm-config/**/*.yamlConfigurationModelImpl with only test modules
# META-INF/hcm-module.yaml
group:
name: test-group
project: test-project
module:
name: test-config
# NO 'config:' key here! ModuleReader discovers by convention.
ModuleReader automatically discovers:
hcm-config/**/*.yaml - Configurationhcm-content/**/*.yaml - Contentnamespaces/**/*.cnd - Node types
# WRONG - config: key is invalid for ModuleReader
module:
name: test-config
config: # <- ERROR: Not valid!
source: /hcm-config
# WRONG - Missing dependencies cause errors
group:
name: test-group
after:
- hippo-cms # <- ERROR: hippo-cms doesn't exist in test env
HCM config uses flat paths, not nested YAML:
# CORRECT - Flat paths
definitions:
config:
/hst:myproject:
jcr:primaryType: hst:hst
/hst:myproject/hst:sites:
jcr:primaryType: hst:sites
/hst:myproject/hst:sites/myproject:
jcr:primaryType: hst:site
# WRONG - Nested structure (creates wrong paths)
definitions:
config:
/hst:myproject:
jcr:primaryType: hst:hst
/hst:sites: # Wrong: creates /hst:sites not /hst:myproject/hst:sites
jcr:primaryType: hst:sites
| Aspect | SkeletonRepository | ConfigServiceRepository |
|---|---|---|
| HST Bootstrap | Minimal hardcoded structure | Full production ConfigService |
| Maintenance | Manual updates when brXM changes | Automatic (uses production code) |
| Structure | Basic hst:hst node only | Complete HST tree |
| Setup | Zero configuration | Requires HCM module + config |
| Speed | Faster (minimal setup) | Slightly slower (full bootstrap) |
| Use Case | Simple unit tests | Integration tests needing real HST |
| Production Parity | No | Yes (exact same code path) |
Cause: Using ClasspathConfigurationModelReader format in hcm-module.yaml
Fix: Remove config: section. ModuleReader discovers by convention.
Cause: Module declares dependency on framework module not present in test
Fix: Remove after: section from group.
Cause: Module group name starts with hippo or onehippo,
or matches your project group — auto-discovery filters it out
Fix: Use dependencyHcmModules = {"module-name"} to force-include it
Cause: An auto-discovered addon module conflicts with your test setup
Fix: Use excludeDependencyHcmModules = {"module-name"} to opt out
Cause: HCM config files missing or in wrong location
Fix:
hcm-config/ directory exists in test resources/hst:myproject (not /hst:hst)ConfigServiceRepository uses pluggable strategies:
canHandle() - checks for META-INF/hcm-module.yamlcanHandle()/hst:hst node onlyStrategy selection is automatic based on classpath resources.
src/test/
├── java/org/example/
│ └── MyIntegrationTest.java
└── resources/
├── META-INF/
│ └── hcm-module.yaml # Module descriptor
├── hcm-config/
│ └── hst/
│ └── demo-hst.yaml # HST configuration
└── org/example/
└── config-service-jcr.xml # Repository override