Back to blog
Dec 28, 2024
3 min read

Configuration driven REST framework - 5.Testing guide (Configuration driven REST framework)

Comprehensive Guide to Strategy Pattern Implementation with Spring Framework

Testing Guide for Configuration-Driven REST Framework

Table of Contents

  1. Unit Testing
  2. Integration Testing
  3. Performance Testing
  4. End-to-End Testing
  5. Test Data Management
  6. Best Practices

Unit Testing

Testing Transformers

Transformers should be tested in isolation to verify correct input/output transformation.

@ExtendWith(MockitoExtension.class)
class RequestTransformerTest {
@InjectMocks
private RequestTransformer transformer;
@Test
void shouldTransformValidInput() {
// Given
String rawInput = "{\"field\": \"value\"}";
// When
TransformedRequest result = transformer.transform(rawInput);
// Then
assertNotNull(result);
assertEquals("value", result.getField());
}
@Test
void shouldHandleInvalidInput() {
// Given
String invalidInput = "invalid json";
// When/Then
assertThrows(TransformationException.class,
() -> transformer.transform(invalidInput));
}
}

Testing Business Logic

Business logic components should be tested with mocked dependencies.

@ExtendWith(MockitoExtension.class)
class BusinessLogicTest {
@Mock
private Repository repository;
@Mock
private ExternalService externalService;
@InjectMocks
private BusinessLogicImpl businessLogic;
@Test
void shouldProcessRequest() {
// Given
Request request = new Request();
when(repository.find(any())).thenReturn(Optional.of(new Entity()));
// When
Response response = businessLogic.execute(request);
// Then
assertNotNull(response);
verify(externalService).notify(any());
}
}

Testing Validation Rules

@ExtendWith(MockitoExtension.class)
class ValidationRulesTest {
@InjectMocks
private ValidationRuleEngine engine;
@Test
void shouldEnforceRateLimit() {
// Given
RateLimitRule rule = new RateLimitRule(10, Duration.ofMinutes(1));
Request request = new Request();
// When/Then
IntStream.range(0, 10).forEach(i ->
assertTrue(engine.validate(request, rule)));
assertThrows(RateLimitExceededException.class,
() -> engine.validate(request, rule));
}
}

Integration Testing

Configuration Testing

@SpringBootTest
class EndpointConfigIntegrationTest {
@Autowired
private ConfigurationLoader loader;
@Autowired
private EndpointValidator validator;
@Test
void shouldLoadAndValidateConfiguration() {
// Given
String configPath = "test-endpoints.json";
// When
List<EndpointConfig> configs = loader.loadConfiguration(configPath);
// Then
assertFalse(configs.isEmpty());
configs.forEach(config -> {
Set<ConstraintViolation<EndpointConfig>> violations =
validator.validate(config);
assertTrue(violations.isEmpty());
});
}
}

Database Integration

@SpringBootTest
@Transactional
class RepositoryIntegrationTest {
@Autowired
private Repository repository;
@Test
void shouldPersistAndRetrieve() {
// Given
Entity entity = new Entity();
// When
Entity saved = repository.save(entity);
Optional<Entity> retrieved = repository.findById(saved.getId());
// Then
assertTrue(retrieved.isPresent());
assertEquals(saved, retrieved.get());
}
}

Performance Testing

JMeter Test Configuration

<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2">
<ThreadGroup guiclass="ThreadGroupGui" testname="API Load Test">
<stringProp name="ThreadGroup.num_threads">100</stringProp>
<stringProp name="ThreadGroup.ramp_time">10</stringProp>
<HTTPSamplerProxy>
<stringProp name="HTTPSampler.path">/api/endpoint</stringProp>
<stringProp name="HTTPSampler.method">POST</stringProp>
<elementProp name="HTTPsampler.Arguments">
<collectionProp name="Arguments.arguments">
<elementProp name="test_data">
<stringProp name="Argument.value">${__FileToString(test_data.json)}</stringProp>
</elementProp>
</collectionProp>
</elementProp>
</HTTPSamplerProxy>
</ThreadGroup>
</jmeterTestPlan>

Performance Metrics Collection

@Test
void performanceTest() {
// Given
int numRequests = 1000;
ExecutorService executor = Executors.newFixedThreadPool(10);
List<Future<Long>> responseTimes = new ArrayList<>();
// When
IntStream.range(0, numRequests).forEach(i -> {
responseTimes.add(executor.submit(() -> {
long start = System.nanoTime();
// Make request
long end = System.nanoTime();
return end - start;
}));
});
// Then
DoubleSummaryStatistics stats = responseTimes.stream()
.map(this::getFuture)
.mapToDouble(t -> t / 1_000_000.0)
.summaryStatistics();
assertTrue(stats.getAverage() < 100.0); // Less than 100ms average
assertTrue(stats.getMax() < 500.0); // Less than 500ms max
}

End-to-End Testing

API Testing

@SpringBootTest
@AutoConfigureMockMvc
class EndToEndApiTest {
@Autowired
private MockMvc mockMvc;
@Test
void shouldProcessRequestEndToEnd() throws Exception {
// Given
String request = """
{
"data": "test",
"metadata": {
"type": "test"
}
}
""";
// When/Then
mockMvc.perform(post("/api/endpoint")
.contentType(MediaType.APPLICATION_JSON)
.content(request))
.andExpect(status().isOk())
.andExpect(jsonPath("$.status").value("SUCCESS"))
.andExpect(jsonPath("$.result").exists());
}
}

Test Data Management

Test Data Builders

public class TestDataBuilder {
public static Request.Builder requestBuilder() {
return Request.builder()
.field1("default")
.field2(123)
.timestamp(LocalDateTime.now());
}
public static EndpointConfig.Builder configBuilder() {
return EndpointConfig.builder()
.path("/test")
.httpMethod("POST")
.requiredRights(List.of("READ", "WRITE"))
.inputTransformer("defaultTransformer")
.businessLogicBean("defaultLogic")
.outputTransformer("defaultOutputTransformer");
}
}

Best Practices

  1. Test Isolation

    • Each test should be independent
    • Use @DirtiesContext when necessary
    • Reset mocks between tests
  2. Test Coverage

    • Aim for > 80% code coverage
    • Focus on business-critical paths
    • Test edge cases and error scenarios
  3. Test Organization

    • Follow AAA pattern (Arrange, Act, Assert)
    • Use descriptive test names
    • Group related tests in nested classes
  4. Performance Testing

    • Run performance tests in isolation
    • Use realistic data volumes
    • Monitor system resources
  5. Maintenance

    • Regular test cleanup
    • Update tests with code changes
    • Document test requirements
  6. CI/CD Integration

    • Automate test execution
    • Set quality gates
    • Generate test reports