plugins/developer-kit-java/skills/unit-test-exception-handler/SKILL.md
Provides patterns for unit testing `@ExceptionHandler` and `@ControllerAdvice` in Spring Boot applications. Validates error response formatting, mocks exceptions, verifies HTTP status codes, tests field-level validation errors, and asserts custom error payloads. Use when writing Spring exception handler tests, REST API error tests, or mocking controller advice.
npx skillsauth add giuseppe-trisciuoglio/developer-kit unit-test-exception-handlerInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
This skill provides patterns for writing unit tests for Spring Boot exception handlers. It covers testing @ExceptionHandler methods in @ControllerAdvice classes using MockMvc, including HTTP status assertions, JSON response validation, field-level validation error testing, and mocking handler dependencies.
@ExceptionHandler methods@ControllerAdvice global exception handling@ExceptionHandlersetControllerAdvice() on MockMvcBuilders.standaloneSetup().andExpect(status().isXxx())jsonPath("$.field") matchersMethodArgumentNotValidException produces field-level details.andDo(print()) — if handler not invoked, verify setControllerAdvice() is called and exception type matches@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public ErrorResponse handleNotFound(ResourceNotFoundException ex) {
return new ErrorResponse(404, "Not Found", ex.getMessage());
}
@ExceptionHandler(ValidationException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ErrorResponse handleValidation(ValidationException ex) {
return new ErrorResponse(400, "Bad Request", ex.getMessage());
}
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ValidationErrorResponse handleMethodArgumentNotValid(MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getFieldErrors().forEach(e -> errors.put(e.getField(), e.getDefaultMessage()));
return new ValidationErrorResponse(400, "Validation Failed", errors);
}
}
public record ErrorResponse(int status, String error, String message) {}
public record ValidationErrorResponse(int status, String error, Map<String, String> errors) {}
@ExtendWith(MockitoExtension.class)
class GlobalExceptionHandlerTest {
private MockMvc mockMvc;
@BeforeEach
void setUp() {
GlobalExceptionHandler handler = new GlobalExceptionHandler();
mockMvc = MockMvcBuilders.standaloneSetup(new TestController())
.setControllerAdvice(handler)
.build();
}
@Test
void shouldReturn404WhenResourceNotFound() throws Exception {
mockMvc.perform(get("/api/users/999"))
.andExpect(status().isNotFound())
.andExpect(jsonPath("$.status").value(404))
.andExpect(jsonPath("$.error").value("Not Found"))
.andExpect(jsonPath("$.message").value("User not found"));
}
@Test
void shouldReturn400WithFieldErrorsOnValidationFailure() throws Exception {
mockMvc.perform(post("/api/users")
.contentType("application/json")
.content("{\"name\":\"\",\"email\":\"invalid\"}"))
.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.status").value(400))
.andExpect(jsonPath("$.errors.name").value("must not be blank"))
.andExpect(jsonPath("$.errors.email").value("must be a valid email"));
}
}
@RestController
@RequestMapping("/api")
class TestController {
@GetMapping("/users/{id}") public User getUser(@PathVariable Long id) {
throw new ResourceNotFoundException("User not found");
}
@PostMapping("/users") public User createUser(@RequestBody @Valid User user) {
throw new ValidationException("Validation failed");
}
}
@ExceptionHandler method independently with a dedicated exception throw@ControllerAdvice instance via setControllerAdvice() — never skip itMockMvcBuilders.standaloneSetup() for isolated handler tests without full Spring context.andDo(print()) to print request/response when a test failssetControllerAdvice() is called on the builder.andDo(print()) to inspect actual response structure@ResponseStatus on the handler method@Order controls precedence; more specific exception types take priority@ExceptionHandler specificity: more specific exception types are matched first; Exception.class catches all unmatched types@ResponseStatus default: without @ResponseStatus or returning ResponseEntity, HTTP status defaults to 200@ExceptionHandler in @ControllerAdvice is global; declared in a controller it is local only to that controllerverify(mockLogger).logXxx(...)MessageSource, test with different Locale values to confirm message resolutionAuthorizationException handlers can access SecurityContextHolder — test that context is correctly evaluateddevelopment
Provides security review capability for TypeScript/Node.js applications, validates code against XSS, injection, CSRF, JWT/OAuth2 flaws, dependency CVEs, and secrets exposure. Use when performing security audits, before deployment, reviewing authentication/authorization implementations, or ensuring OWASP compliance for Express, NestJS, and Next.js. Triggers on "security review", "check for security issues", "TypeScript security audit".
development
Provides final code cleanup after task review approval. Removes debug logs, temporary comments, dead code, optimizes imports, and improves readability. Use when asked to clean up code, polish, finalize, tidy up, remove technical debt, or prepare code for completion after review. Not for refactoring logic or fixing bugs—focused solely on cosmetic and hygiene cleanup.
tools
Ralph Wiggum-inspired automation loop for specification-driven development. Orchestrates task implementation, review, cleanup, and synchronization using a Python script. Use when: user runs /loop command, user asks to automate task implementation, user wants to iterate through spec tasks step-by-step, or user wants to run development workflow automation with context window management. One step per invocation. State machine: init → choose_task → implementation → review → fix → cleanup → sync → update_done. Supports --from-task and --to-task for task range filtering. State persisted in fix_plan.json.
testing
Creates, updates, validates, and displays the architectural DNA of a project through two shared documents: docs/specs/architecture.md (technology stack, architectural rules, security constraints, AI guardrails) and docs/specs/ontology.md (domain glossary / Ubiquitous Language). Use BEFORE brainstorm as a project setup step, or at any point in the SDD lifecycle to validate specs/tasks against architecture principles. Triggers on 'create constitution', 'update constitution', 'constitution check', 'validate against constitution', 'project principles', 'architectural guardrails', 'setup project architecture', 'define ontology'.