我有这个非常简单的控制器类和一个简单的(jpa)存储库。 我想做的是测试它的api,但模拟它的存储库,并根据测试用例让它返回对象或不返回对象。
我现在的问题是我不知道该怎么做。
我知道如何使用@Mock / @InjectMocks / when()。return()
模拟存储库并将其注入到控制器/服务类中。但是当我想对MockMvc请求后也要这样做时,我失败了。 任何帮助都将受到高度赞赏
控制器
import java.util.Optional;
@RestController
@Slf4j
public class ReceiptController implements ReceiptsApi {
@Autowired
private ReceiptRepository receiptRepository;
@Autowired
private ErrorResponseExceptionFactory exceptionFactory;
@Autowired
private ApiErrorResponseFactory errorResponseFactory;
@Override
public Receipt getReceipt(Long id) {
Optional<ReceiptEntity> result = receiptRepository.findById(id);
if (result.isEmpty()) {
throw invalid("id");
}
ReceiptEntity receipt = result.get();
return Receipt.builder().id(receipt.getId()).purchaseId(receipt.getPurchaseId()).payload(receipt.getHtml()).build();
}
private ErrorResponseException invalid(String paramName) {
return exceptionFactory.errorResponse(
errorResponseFactory.create(HttpStatus.NOT_FOUND.value(), "NOT_VALID", String.format("receipt with id %s not found.", paramName))
);
}
}
这是测试班
@WebMvcTest(ReceiptController.class)
@RestClientTest
public class ReceiptControllerTest {
@InjectMocks
private ReceiptController receiptController;
@Mock
private ReceiptRepository receiptRepository;
@Mock
private ErrorResponseExceptionFactory exceptionFactory;
@Mock
private ApiErrorResponseFactory errorResponseFactory;
private MockMvc mvc;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mvc = MockMvcBuilders.standaloneSetup(
new ReceiptController())
.build();
}
@Test
public void getReceiptNotFoundByRequest() throws Exception {
mvc.perform(MockMvcRequestBuilders
.get("/receipt/1")
.header("host", "localhost:1348")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isNotFound());
}
//TODO: Finish this test
@Test
public void getReceiptFoundByRequest() throws Exception {
ReceiptEntity receipt1 = ReceiptEntity.builder().id(99999L).purchaseId(432L).html("<html>").build();
when(receiptRepository.findById(1L)).thenReturn(Optional.of(ReceiptEntity.builder().id(1L).purchaseId(42L).html("<html></html>").build()));
ResultActions result = mvc.perform(get("/receipt/1")
.header("host", "localhost:1348")
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}
答案 0 :(得分:0)
在您的setUp()
方法中,您正在使用Mockito模拟用@Mock
注释的bean,并将它们注入新的ReceiptController
实例中,然后将其放入带有注释的字段中与@InjectMocks
。
在下一行,您将使用新的ReceiptController
实例(您使用new ReceiptController()
)来设置MockMvc,而不是使用Mockito创建的实例。这意味着该实例中的所有字段均为null
。
这几乎可以归结为Why is my Spring @Autowired
field null
。
要解决此问题,您可以将receiptController
传递给MockMvc。在这种情况下,您还可以删除@WebMvcTest
和@RestClientTest
,因为您没有使用它们。
或者,您可以使用@RunWith(SpringRunner.class)
设置测试,并通过使用@Autowired
代替@InjectMocks
和@MockBean
代替{{ 1}}。在那种情况下,您根本不需要@Mock
方法,因为MockMvc可以由Spring自动接线。
答案 1 :(得分:0)
@WebMvcTest和MockMvc允许您执行集成测试,而不是单元测试。
它们允许您引导实际的Spring应用程序(使用随机端口)并使用其依赖项测试实际的控制器类。这意味着您在测试顶部声明的变量实际上并未在当前设置中使用。
如果希望对控制器进行单元测试,则可以删除@WebMvcTest,创建模拟依赖项(就像您所做的那样),然后直接调用方法,而不使用MockMvc。
如果您确实希望编写集成测试,则需要模拟外部依赖关系(例如数据库)。您可以将spring配置为在测试环境中使用嵌入式数据库(例如H2),以免影响实际数据库。
在此处查看示例:https://www.baeldung.com/spring-testing-separate-data-source