使用Spring引导和JUnit从控制器类模拟存储库
我正在为我的控制器类编写测试用例,它是一个Spring引导应用程序,我只想为调用服务和服务到存储库的控制器类编写测试用例。我正在使用SpringBootTest,它用于为我的所有bean创建实例。我只想模拟数据库调用和外部api调用使用Spring引导和JUnit从控制器类模拟存储库,junit,mocking,mockito,powermock,spring-boot-test,Junit,Mocking,Mockito,Powermock,Spring Boot Test,我正在为我的控制器类编写测试用例,它是一个Spring引导应用程序,我只想为调用服务和服务到存储库的控制器类编写测试用例。我正在使用SpringBootTest,它用于为我的所有bean创建实例。我只想模拟数据库调用和外部api调用 MyController { @Autowired MyService service; //Api call for getDetails service.getDetails(); } MyService { @A
MyController {
@Autowired
MyService service;
//Api call for getDetails
service.getDetails();
}
MyService {
@Autowired
MyRepository repo;
}
MyControllertest {
@Autowired
MyController controller;
@Mock
MyRepository repoMock;
@Before
public void setup(){
// intit mocks
}
@Test
public void myTest(){
when(repoMock.getDetails()).thenReturn(null);
controller.getdetails();
}
}
当我运行测试用例时,它不是使用模拟存储库,而是使用服务类中提到的@Autowired
存储库
谁能解释一下如何从控制器类模拟存储库
在这个博客上发布了这么多问题,但我没有得到任何回复。你不能打电话给我
MockitoAnnotations.initMocks(this);
在
setUp
方法中,因为SpringRunner
已经初始化模拟。通过调用initmock
可以将一个新对象分配给repoMock
。所有Mockito操作,如when
都在新对象上执行,但是Spring仍然使用由SpringRunner
创建的“旧”MyRepository
,它没有使用您的mock,因为您没有将这些mock注入控制器/服务类。相反,您正在对其进行自动布线。正确的做法是
@RunWith(MockitoJUnitRunner.class)
public class MyControllerTest {
@InjectMocks
MyController controller;
.....
}
更好的解决方案是去掉字段注入,使用构造函数注入
例如,在控制器和服务类中。您可以在Contructor上执行该操作,而不是在字段上使用@Autowired。比如说
class MyService {
private final MyRepository repo;
@Autowired
public MyService(final MyRepository repo) {
this.repo = repo;
}
}
在控制器类中也是如此
class MyController {
@Autowired
private final MyService service;
public MyController(final MyService service) {
this.service = service
}
}
这样做可以帮助您在运行时轻松设置模拟。例如,在您的测试类中,您可以
@RunWith(MockitoJUnitRunner.class)
public class MyControllertest {
MyController controller;
MyService service;
@Mock
MyRepository repoMock;
@Before
public void setup(){
// init mocks
service = new MyService(repoMock);
controller = new MyController(service);
}
..............
}
这是一篇关于字段注入的好文章3种解决方法:
一个是莫基托
@Mock
MyRepository repoMock;
@InjectMocks
MyService service;
它的作用是按类类型匹配MyService类字段,并将模拟变量分配给这些字段。因此,它将模拟注入到您的服务中。这与春天无关
2.重构MyService构造函数,以便通过构造函数注入依赖项@Autowired也可以与构造函数一起使用,因此它是首选的方法
@Autowired
public MyService(MyRespoitory repository){
this.repository = repository;
}
3.使用弹簧测试环境:
并定义一个bean,它是存储库的模拟版本
@Bean
public MyRepository mockMyRepository(){
return mock(MyRespository.class);
}
对于服务测试来说,这可能会很慢,也很烦人
第二种方法是最受欢迎和最简单的方法。这对我很有用:
@MockBean
private OrderRepository orderRepository;
@SpyBean
private OrderService orderService;
@InjectMocks
private OrderController orderController;
这很好:
@WebMvcTest(MyController.class)
public class WebMockTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private MyRepository repository;
@SpyBean
private MyService service;
@InjectMocks
private MyController controller;
@Test
public void saveTest() throws Exception {
String content = "";
long id=Long.MAX_VALUE;
Entity entityToSend = new Entity(id, content);
Gson gson = new Gson();
when(repository.save(any(Entity.class)).thenReturn(Optional.of(entityToSend));
mockMvc.perform(MockMvcRequestBuilders
.post("/api/save")
.content(gson.toJson(entityToSend))
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$.id").value(Long.MAX_VALUE))
.andExpect(MockMvcResultMatchers.jsonPath("$.content").value(content));
}
我正在使用MockitoAnnotations.initMocks(this);在我的设置方法中。我仍然无法在我的服务类中获取MockRepository,它仍然使用@Autowired one。您不能使用
initMocks
。请您向我们展示MyControllertest
的类级注释。例如,@RunWith(SpringJUnit4ClassRunner.class)
。请提供有关您的程序的更多说明,以及可能的问题说明。如何修复它。当你的帖子缺少描述时,对大多数人来说都是无用的。请按照@DanielHornik的说明操作!