Testing Guide for Configuration-Driven REST Framework
Table of Contents
- Unit Testing
- Integration Testing
- Performance Testing
- End-to-End Testing
- Test Data Management
- 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
@SpringBootTestclass 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@Transactionalclass 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
@Testvoid 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@AutoConfigureMockMvcclass 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
-
Test Isolation
- Each test should be independent
- Use @DirtiesContext when necessary
- Reset mocks between tests
-
Test Coverage
- Aim for > 80% code coverage
- Focus on business-critical paths
- Test edge cases and error scenarios
-
Test Organization
- Follow AAA pattern (Arrange, Act, Assert)
- Use descriptive test names
- Group related tests in nested classes
-
Performance Testing
- Run performance tests in isolation
- Use realistic data volumes
- Monitor system resources
-
Maintenance
- Regular test cleanup
- Update tests with code changes
- Document test requirements
-
CI/CD Integration
- Automate test execution
- Set quality gates
- Generate test reports