使用Spring引导和JUnit从控制器类模拟存储库

使用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

我正在为我的控制器类编写测试用例,它是一个Spring引导应用程序,我只想为调用服务和服务到存储库的控制器类编写测试用例。我正在使用SpringBootTest,它用于为我的所有bean创建实例。我只想模拟数据库调用和外部api调用

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的说明操作!