
Unit Test Controller Layer
Apply MockMvc-focused patterns to unit-test Spring REST controllers including status codes, security, and multipart uploads.
Overview
Unit Test Controller Layer is an agent skill for the Ship phase that guides MockMvc unit tests for Spring REST controllers including auth and upload cases.
Install
npx skills add https://github.com/giuseppe-trisciuoglio/developer-kit --skill unit-test-controller-layerWhat is this skill?
- Multiple status code scenarios with mocked UserService (200, 404, 401)
- Role-based access tests expecting 403 on admin delete without authority
- Bearer token authentication tests for protected routes
- File upload testing with MockMultipartFile on controller endpoints
- Java Spring MockMvc perform/andExpect patterns ready to paste
Adoption & trust: 1.2k installs on skills.sh; 271 GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
Your REST controllers lack consistent tests for 401/403/404 branches, security rules, and file uploads.
Who is it for?
Solo developers on Spring Boot APIs who want controller tests without full integration suites.
Skip if: Non-Java stacks, pure service-layer testing with no HTTP surface, or E2E browser flows.
When should I use this skill?
You are writing or extending Spring MVC controller tests with MockMvc, security, or file upload scenarios.
What do I get? / Deliverables
You add focused MockMvc tests that mock services and verify HTTP status and security behavior per endpoint.
- JUnit test classes with status, security, and upload coverage patterns
Recommended Skills
Journey fit
How it compares
Controller-layer MockMvc tests instead of only integration tests or untested HTTP glue code.
Common Questions / FAQ
Who is unit-test-controller-layer for?
Indie backend builders using Spring who need reliable HTTP and security assertions at the controller boundary.
When should I use unit-test-controller-layer?
In Ship testing while adding or refactoring REST endpoints, before release, or when fixing auth regressions.
Is unit-test-controller-layer safe to install?
Check the Security Audits panel on this page; the skill is documentation and test snippets with no bundled network calls.
SKILL.md
READMESKILL.md - Unit Test Controller Layer
# Advanced Controller Testing Patterns ## Multiple Status Code Scenarios ```java @Test void shouldReturnDifferentStatusCodesForDifferentScenarios() throws Exception { // Successful response when(userService.getUserById(1L)).thenReturn(new UserDto(1L, "Alice")); mockMvc.perform(get("/api/users/1")) .andExpect(status().isOk()); // Not found when(userService.getUserById(999L)) .thenThrow(new UserNotFoundException("Not found")); mockMvc.perform(get("/api/users/999")) .andExpect(status().isNotFound()); // Unauthorized mockMvc.perform(get("/api/admin/users")) .andExpect(status().isUnauthorized()); } ``` ## Security Testing ### Testing Role-Based Access ```java @Test void shouldReturn403WhenUserLacksRole() throws Exception { mockMvc.perform(delete("/api/admin/users/1")) .andExpect(status().isForbidden()); } ``` ### Testing Authentication ```java @Test void shouldReturn401WhenNoTokenProvided() throws Exception { mockMvc.perform(get("/api/protected")) .andExpect(status().isUnauthorized()); } @Test void shouldReturn200WhenValidTokenProvided() throws Exception { when(authService.validateToken("valid-token")).thenReturn(true); when(userService.getCurrentUser()).thenReturn(new UserDto(1L, "Alice")); mockMvc.perform(get("/api/protected") .header("Authorization", "Bearer valid-token")) .andExpect(status().isOk()); } ``` ## File Upload Testing ### MockMultipartFile ```java @Test void shouldUploadFileSuccessfully() throws Exception { byte[] fileContent = "test file content".getBytes(); MockMultipartFile file = new MockMultipartFile( "file", "test.txt", "text/plain", fileContent); when(fileService.store(any())).thenReturn("stored-file-id"); mockMvc.perform(multipart("/api/files/upload").file(file)) .andExpect(status().isOk()) .andExpect(jsonPath("$.fileId").value("stored-file-id")); verify(fileService).store(any(MultipartFile.class)); } ``` ## Pagination ### Testing Paginated Responses ```java @Test void shouldReturnPaginatedUsers() throws Exception { Page<UserDto> page = new PageImpl<>( List.of(new UserDto(1L, "Alice"), new UserDto(2L, "Bob")), PageRequest.of(0, 10), 2); when(userService.getUsers(any(Pageable.class))).thenReturn(page); mockMvc.perform(get("/api/users") .param("page", "0") .param("size", "10")) .andExpect(status().isOk()) .andExpect(jsonPath("$.content").isArray()) .andExpect(jsonPath("$.content.length()").value(2)) .andExpect(jsonPath("$.totalElements").value(2)) .andExpect(jsonPath("$.totalPages").value(1)); } ``` ## Exception Handling ### Custom Exception Handlers ```java @Test void shouldHandleValidationException() throws Exception { when(userService.createUser(any())) .thenThrow(new MethodArgumentNotValidException( null, new BeanPropertyBindingResult(null, "user"))); mockMvc.perform(post("/api/users") .contentType("application/json") .content("{\"invalid\":\"data\"}")) .andExpect(status().isBadRequest()) .andExpect(jsonPath("$.errors").isArray()); } ``` ## Testing Async Endpoints ```java @Test void shouldHandleAsyncResponses() throws Exception { CompletableFuture<UserDto> futureUser = CompletableFuture.completedFuture( new UserDto(1L, "Alice")); when(userService.getUserAsync(1L)).thenReturn(futureUser); MvcResult result = mockMvc.perform(get("/api/users/async/1")) .andExpect(status().isOk()) .andReturn(); // For actual async testing, use withAsyncDispatch() String content = result.getResponse().getContentAsString(); assertThat(content).contains("Alice"); } ``` # Common Pitfalls in Controller Testing ## Testing Business Logic in Controller **Don't**: Test business logic in controller tests **Do**: Keep controller tests focused on HTTP handling only ```java // BAD - Testing business logic @Test void shouldCalculateTotalCorrectly() throws Exception { // This should be in service tests mockMvc.perform(get