JUnit Mockito测试Rest API时ApplicationContext加载失败求助
Hey there! Let's break down why your ApplicationContext is failing to load and fix your test case step by step.
1. Annotation Conflicts Are Breaking Context Loading
Your test class has conflicting annotations that are confusing Spring's context initialization:
@WebMvcTestis purpose-built for testing Spring MVC controllers and automatically usesSpringRunnerunder the hood—you don't need to add@RunWith(SpringJUnit4ClassRunner.class)explicitly.- When using
@WebMvcTest, Spring creates a sliced context containing only web-related beans (like yourItemController). But you're mixing it with@InjectMocks, a Mockito annotation for manually injecting mocks into a class. This creates a conflict because Spring is already trying to manage theItemControllerbean, and you're trying to instantiate a separate instance with Mockito.
2. Your Mocking Approach Is Misaligned
Right now, you're mocking the ItemController method directly, which defeats the point of testing the controller. You should mock the controller's dependency (ItemService) instead, then verify that the controller correctly uses this dependency to handle requests.
Fix Option 1: Proper Integration Test with @WebMvcTest
This tests the actual HTTP endpoint behavior using Spring's MockMvc to simulate requests:
package com.example.demo.controller; import com.example.demo.entities.Item; import com.example.demo.service.ItemService; import org.junit.Test; import org.junit.runner.RunWith; 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.context.junit4.SpringRunner; 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.*; @RunWith(SpringRunner.class) @WebMvcTest(ItemController.class) public class ItemControllerTest { @Autowired private MockMvc mockMvc; // Use @MockBean to add a mocked ItemService to Spring's sliced context @MockBean private ItemService itemService; @Test public void itemGetById_ReturnsExpectedItem() throws Exception { // Arrange: Set up test data and mock service behavior Item testItem = new Item(); testItem.setItemId(10L); testItem.setItemName("Wireless Mouse"); testItem.setItemPrice(49); when(itemService.getItem(10L)).thenReturn(testItem); // Act & Assert: Simulate GET request and validate response mockMvc.perform(get("/item/get") .param("id", "10")) .andExpect(status().isOk()) .andExpect(jsonPath("$.itemId").value(10)) .andExpect(jsonPath("$.itemName").value("Wireless Mouse")) .andExpect(jsonPath("$.itemPrice").value(49)); } }
Fix Option 2: Pure Unit Test (No Spring Context)
If you just want to test the controller's core logic without loading a Spring context, use Mockito's runner instead:
package com.example.demo.controller; import com.example.demo.entities.Item; import com.example.demo.service.ItemService; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.class) // Use Mockito's runner instead of Spring public class ItemControllerUnitTest { @Mock private ItemService itemService; @InjectMocks private ItemController itemController; @Test public void getById_ReturnsItemFromService() { // Arrange Item testItem = new Item(); testItem.setItemId(10L); when(itemService.getItem(10L)).thenReturn(testItem); // Act Item result = itemController.getById(10L); // Assert assertEquals(testItem, result); } }
Quick Extra Notes
- In your
ItemController, thegetByIdmethod uses@GetMapping("/get")with an unannotatedidparameter. This expects a query parameter like?id=10. If you want a path variable (e.g.,/item/get/10), update the method to:
Then adjust your test to use@GetMapping("/get/{id}") public Item getById(@PathVariable Long id) { return itemService.getItem(id); }get("/item/get/10")instead of adding a query param. - Your
ItemServiceusesrepository.findById(id).get()which throws aNoSuchElementExceptionif the item doesn't exist. Consider adding error handling (like returningnullor throwing a custom exception) and a test case for that scenario.
内容的提问来源于stack exchange,提问作者Pankaj Yadav

