You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JUnit测试带@HttpStatus的自定义异常及方法改造求助

Great call deciding not to test private methods directly—you’re already thinking about good unit testing practices! Testing internal implementation details leads to brittle tests that break easily when you refactor, so exposing the logic as a public method in your Service is the right approach. Let’s walk through how to set this up properly with JUnit 5 (the latest standard) and cover both Service-level and Controller-level tests.

Step 1: Adjust Your Service Method Visibility

First, refactor that private method in your Service class to be public (or package-private if you want to limit visibility slightly, but public is cleaner for explicit testability). For example:

@Service
public class YourDataService {

    // Other core service methods...

    // Refactored from private to public
    public void validateResult(DataResult result) throws EmptyResultException {
        if (result == null || result.getItems().isEmpty()) {
            throw new EmptyResultException("No matching data found for your request");
        }
    }
}
Step 2: Write a JUnit 5 Test for the Service

Use JUnit Jupiter's Assertions.assertThrows() to verify your custom exception is thrown when the conditions are met. If your Service has dependencies (like a Repository), use Mockito to mock those out and isolate the logic you're testing.

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.junit.jupiter.MockitoExtension;

import static org.junit.jupiter.api.Assertions.*;

@ExtendWith(MockitoExtension.class)
public class YourDataServiceTest {

    @InjectMocks
    private YourDataService dataService; // Mockito injects mocked dependencies automatically

    @Test
    void validateResult_WhenResultIsEmpty_ThrowsEmptyResultException() {
        // Arrange: Create an empty result object
        DataResult emptyResult = new DataResult();

        // Act & Assert: Verify the exception is thrown
        EmptyResultException exception = assertThrows(EmptyResultException.class, () -> {
            dataService.validateResult(emptyResult);
        });

        // Optional: Verify the exception message matches your expectation
        assertEquals("No matching data found for your request", exception.getMessage());
    }

    @Test
    void validateResult_WhenResultIsValid_DoesNotThrowException() {
        // Arrange: Create a valid, non-empty result
        DataResult validResult = new DataResult();
        validResult.getItems().add(new DataItem("sample-content"));

        // Act & Assert: Confirm no exception is thrown
        assertDoesNotThrow(() -> {
            dataService.validateResult(validResult);
        });
    }
}

Since you have a RestController that calls this Service, you should also test that the Controller handles the exception correctly (e.g., returns a 404 Not Found status code). Use Spring's MockMvc for this integration test:

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.web.servlet.MockMvc;

import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@WebMvcTest(YourDataController.class)
public class YourDataControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private YourDataService dataService; // Mock the Service to control its behavior

    @Test
    void getData_WhenServiceThrowsEmptyResult_ReturnsNotFound() throws Exception {
        // Arrange: Make the Service throw your exception when called
        when(dataService.fetchAndValidateData("user-123"))
                .thenThrow(new EmptyResultException("No data found"));

        // Act & Assert: Verify the Controller returns the expected HTTP status
        mockMvc.perform(get("/api/data/user-123"))
                .andExpect(status().isNotFound());
    }
}
Quick Additional Notes
  • If you really don’t want to make the method fully public, you can use package-private visibility (remove the private modifier, no access specifier). Just place your test class in the same package as the Service, and it will be able to call the method without needing PowerMock.
  • Avoid PowerMock unless absolutely necessary—it bypasses Java’s access controls and can make tests harder to maintain. Your initial instinct to refactor instead is far better for long-term test health.

内容的提问来源于stack exchange,提问作者John

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 10:28:11