<!-- JUnit 5 (Required for annotation-based testing) -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.2</version>
<scope>test</scope>
</dependency>
<!-- For JAX-RS / Page Model API Testing -->
<dependency>
<groupId>org.bloomreach.forge.brut</groupId>
<artifactId>brut-resources</artifactId>
<version>${brut.version}</version>
<scope>test</scope>
</dependency>
<!-- For Component Testing -->
<dependency>
<groupId>org.bloomreach.forge.brut</groupId>
<artifactId>brut-components</artifactId>
<version>${brut.version}</version>
<scope>test</scope>
</dependency>
Warning: <scope>test</scope> is required.
BRUT replaces core HST beans (pipelines, component manager, link creator, etc.) with test-oriented
implementations. If BRUT is on the runtime classpath without test scope, its mock beans will shadow
production beans and real HST endpoints will stop working.
Note: Most brXM projects already include JUnit 5 via the parent POM.
package org.example;
import org.bloomreach.forge.brut.resources.annotation.BrxmJaxrsTest;
import org.bloomreach.forge.brut.resources.annotation.DynamicJaxrsTest;
import org.example.rest.HelloResource;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
@BrxmJaxrsTest(resources = {HelloResource.class}) // beanPackages auto-detected!
public class MyApiTest {
@Test
void testEndpoint(DynamicJaxrsTest brxm) { // Parameter injection - no IDE warnings!
String response = brxm.request()
.get("/site/api/hello/world")
.execute();
assertEquals("Hello, World!", response);
}
}
package org.example;
import org.bloomreach.forge.brut.resources.annotation.BrxmPageModelTest;
import org.bloomreach.forge.brut.resources.annotation.DynamicPageModelTest;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
@BrxmPageModelTest // Zero-config for standard project layouts
public class MyPageModelTest {
@Test
void testComponent(DynamicPageModelTest brxm) {
PageModelResponse pageModel = brxm.request()
.get("/site/resourceapi/news")
.executeAsPageModel();
assertNotNull(pageModel.getRootComponent());
}
}
package org.example;
import org.bloomreach.forge.brut.components.annotation.BrxmComponentTest;
import org.bloomreach.forge.brut.components.annotation.DynamicComponentTest;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
@BrxmComponentTest // beanPackages, nodeTypes auto-detected
public class MyComponentTest {
@Test
void testComponent(DynamicComponentTest brxm) {
assertNotNull(brxm.getHstRequest());
assertNotNull(brxm.getHstResponse());
assertTrue(brxm.getRootNode().hasNode("hippo:configuration"));
}
}
Node Type Auto-Detection: When nodeTypes is not specified,
types are automatically detected from @Node(jcrType="...") annotations in bean classes.
Use explicit nodeTypes only for inheritance ("ns:Child extends ns:Parent")
or types outside scanned packages.
@BrxmJaxrsTest(resources = {HelloResource.class})
public class MyApiTest {
@SuppressWarnings("unused") // Injected by extension
private DynamicJaxrsTest brxm;
@BeforeEach
void setup() {
brxm.getHstRequest().setHeader("X-Custom", "value");
}
@Test
void testEndpoint() {
brxm.request().get("/site/api/hello").assertBody("Hello");
}
}
@BrxmJaxrsTest(resources = {HelloResource.class, NewsResource.class})
// beanPackages auto-detected from project-settings.xml or pom.xml
@BrxmJaxrsTest(
// Optional: Override auto-detected bean packages
beanPackages = {"org.example.model"},
// JAX-RS resources (recommended - eliminates Spring XML)
resources = {HelloResource.class},
// Custom YAML patterns (auto-detected from <package>/imports/)
yamlPatterns = {"classpath*:custom/**/*.yaml"},
// Custom CND patterns
cndPatterns = {"classpath*:custom/**/*.cnd"},
// Load production HCM content
loadProjectContent = true,
// Override auto-detected values
hstRoot = "/hst:myproject",
springConfigs = {"/org/example/custom.xml"},
addonModules = {"/org/example/addon"}
)
Use zero-config (recommended) when:
project-settings.xml → beanPackages auto-detected<package>/imports/ → auto-detectedSpecify parameters when:
beanPackages - auto-detection fails or you need different packagesresources - to register JAX-RS endpoints without Spring XMLspringConfigs - resources need Spring-managed dependencieshstRoot - HST root doesn't match Maven artifactId
@BrxmComponentTest(
beanPackages = {"org.example.beans"},
content = "/test-content.yaml", // YAML content to import
contentRoot = "/content/documents", // Where to import + sets site base
nodeTypes = {"ns:Type extends ns:Base"} // Optional - auto-detected from @Node
)
@Test
void testWithFluentApi(DynamicJaxrsTest brxm) {
String response = brxm.request()
.get("/site/api/news") // Sets URI and GET method
.withHeader("X-Custom", "value") // Add custom header
.queryParam("category", "tech") // Add query parameter
.execute(); // Execute and get response as String
assertTrue(response.contains("news"));
}
// One-liner assertion
@Test
void testAssertBody(DynamicJaxrsTest brxm) {
brxm.request()
.get("/site/api/hello/world")
.assertBody("Hello, World!"); // Assert response body directly
}
// JSON deserialization in one line
@Test
void testTypedResponse(DynamicJaxrsTest brxm) {
User user = brxm.request()
.get("/site/api/user/123")
.executeAs(User.class); // Execute and deserialize JSON
assertEquals("John", user.getName());
}
// Response with status code
@Test
void testResponseWithStatus(DynamicJaxrsTest brxm) {
Response<User> response = brxm.request()
.get("/site/api/users/123")
.executeWithStatus(User.class); // Get status + typed body
assertEquals(200, response.status());
assertTrue(response.isSuccessful());
assertEquals("John", response.body().getName());
}
// POST with JSON string
@Test
void testPostRequest(DynamicJaxrsTest brxm) {
User created = brxm.request()
.post("/site/api/users")
.withJsonBody("{\"name\": \"John\"}") // Sets body + Content-Type
.executeAs(User.class);
assertEquals("John", created.getName());
}
// POST with object serialization
@Test
void testPostWithObject(DynamicJaxrsTest brxm) {
User input = new User("Jane", 25);
User created = brxm.request()
.post("/site/api/users")
.withJsonBody(input) // Auto-serializes to JSON
.executeAs(User.class);
assertEquals("Jane", created.getName());
}
// Authenticated user with roles
@Test
void testProtectedEndpoint(DynamicJaxrsTest brxm) {
String response = brxm.request()
.get("/site/api/admin/users")
.asUser("admin", "admin", "editor") // username, roles...
.execute();
assertThat(response).contains("users");
}
// Role-only (no username needed)
@Test
void testRoleBasedAccess(DynamicJaxrsTest brxm) {
String response = brxm.request()
.get("/site/api/reports")
.withRole("manager", "viewer") // roles only
.execute();
assertThat(response).contains("reports");
}
@Test
void testPageModelApi(DynamicPageModelTest brxm) throws Exception {
PageModelResponse pageModel = brxm.request()
.get("/site/resourceapi/")
.executeAsPageModel(); // Parse response as PageModelResponse
// Navigate component tree
PageComponent root = pageModel.getRootComponent();
assertNotNull(root);
// Find component by name
PageComponent header = pageModel.findComponentByName("header").orElseThrow();
// Get children
List<PageComponent> children = pageModel.getChildComponents(root);
}
@Test
void testRepositoryAccess(DynamicJaxrsTest brxm) {
try (RepositorySession session = brxm.repository()) {
session.assertNodeExists("/hst:myproject")
.assertNodeExists("/hippo:configuration");
Node newsNode = session.getNode("/content/documents/news");
assertEquals("hippo:handle", newsNode.getPrimaryNodeType().getName());
}
// Auto-cleanup on try-with-resources exit
}
@BrxmComponentTest(
beanPackages = {"org.example.beans"},
content = "/test-content.yaml",
contentRoot = "/content/documents/myproject"
)
class MyComponentTest {
@Test
void testComponent(DynamicComponentTest brxm) {
// Node types auto-detected from @Node annotations!
// Content imported automatically from annotation parameters
MyComponent component = new MyComponent();
component.init(null, brxm.getComponentConfiguration());
component.doBeforeRender(brxm.getHstRequest(), brxm.getHstResponse());
MyModel model = brxm.getRequestAttributeValue("model");
assertNotNull(model);
}
}
@Test
void testComponentWithParameters(DynamicComponentTest brxm) {
// Mock the ParameterInfo interface
MyComponentInfo paramInfo = mock(MyComponentInfo.class);
when(paramInfo.getDocument()).thenReturn("articles/my-article");
when(paramInfo.getPageSize()).thenReturn(10);
// Set on request
brxm.setComponentParameters(paramInfo);
// Execute component
component.doBeforeRender(brxm.getHstRequest(), brxm.getHstResponse());
// Assert on model attributes
MyModel model = brxm.getRequestAttributeValue("model");
assertThat(model).isNotNull();
}
@Test
void testWithRequestParameters(DynamicComponentTest brxm) {
// Add request parameters
brxm.addRequestParameter("page", "2");
brxm.addRequestParameter("sort", "date");
component.doBeforeRender(brxm.getHstRequest(), brxm.getHstResponse());
}
@Test
void testWithSession(DynamicComponentTest brxm) {
// Get or create session (lazy initialization)
HttpSession session = brxm.getHstRequest().getSession();
session.setAttribute("user", new User("John"));
component.doBeforeRender(brxm.getHstRequest(), brxm.getHstResponse());
// Verify session was used
assertThat(session.getAttribute("loginCount")).isEqualTo(1);
}
// Mock session for more control
@Test
void testWithMockedSession(DynamicComponentTest brxm) {
HttpSession session = mock(HttpSession.class);
when(session.getAttribute("user")).thenReturn(new User("Jane"));
brxm.getHstRequest().setSession(session);
component.doBeforeRender(brxm.getHstRequest(), brxm.getHstResponse());
}
Session Isolation: Sessions are automatically invalidated between tests for JAX-RS/PageModel tests.
@BrxmJaxrsTest(
beanPackages = {"org.example.model"},
loadProjectContent = true // Uses HCM modules from classpath
)
src/test/resources/
├── META-INF/
│ └── hcm-module.yaml
├── hcm-config/
│ └── hst/
│ ├── configurations.yaml
│ ├── hosts.yaml
│ └── sites.yaml
└── hcm-content/
└── content/
└── documents/
See ConfigService Repository Guide for detailed setup.
| Parameter | Type | Default | Description |
|---|---|---|---|
beanPackages |
String[] | auto-detected | HST content bean packages (from project-settings.xml or classpath scan) |
resources |
Class<?>[] | {} | JAX-RS resource classes (recommended - eliminates Spring XML) |
hstRoot |
String | auto-detected | HST configuration root (from Maven artifactId) |
springConfigs |
String[] | auto-detected | Spring XML configs (only for Spring-managed dependencies) |
yamlPatterns |
String[] | auto-detected | YAML patterns (from <testPackage>/imports/**/*.yaml) |
cndPatterns |
String[] | {} | CND node type definition patterns |
loadProjectContent |
boolean | true | Load real HCM modules via ConfigServiceRepository |
| Parameter | Type | Default | Description |
|---|---|---|---|
beanPackages |
String[] | auto-detected | HST content bean packages |
hstRoot |
String | auto-detected | HST configuration root |
springConfig |
String | auto-detected | Spring XML config for PageModel pipeline |
loadProjectContent |
boolean | false | Load real HCM modules via ConfigServiceRepository |
| Parameter | Type | Default | Description |
|---|---|---|---|
beanPackages |
String[] | auto-detected | HST content bean packages |
nodeTypes |
String[] | auto-detected | Node types (from @Node annotations in beanPackages) |
content |
String | "" | Classpath path to YAML file with test content |
contentRoot |
String | "" | JCR path where content is imported + sets site base |
| Setting | Auto-Detection Strategy | Override |
|---|---|---|
| Bean Packages | 1. project-settings.xml 2. @Node classpath scan 3. pom.xml groupId 4. test class package |
beanPackages = {"org.example.beans"} |
| HST Root | /hst:${artifactId} from pom.xml |
hstRoot = "/hst:customname" |
| Spring Configs | JAX-RS: custom-jaxrs.xml, rest-resources.xml PageModel: custom-pagemodel.xml |
springConfigs = {"/path/to/config.xml"} |
| Node Types | Scans @Node(jcrType="...") in beanPackages | nodeTypes = {"ns:Type extends ns:Base"} |
| Test YAML | <testPackage>/imports/**/*.yaml | yamlPatterns = {"classpath*:custom/**/*.yaml"} |
| Issue | Fix |
|---|---|
NullPointerException on brxm |
Use parameter injection: void test(DynamicJaxrsTest brxm) |
| "Missing @BrxmJaxrsTest annotation" | Add annotation to test class |
| "Bean packages: NONE" warning | Verify project-settings.xml exists, or add explicit beanPackages |
| Spring config not found | Verify file exists, use absolute path starting with / |
| HST root not found | Override with hstRoot = "/hst:actualname" |
| IDE "field never assigned" warning | Use parameter injection (recommended) or add @SuppressWarnings("unused") |