【问题标题】:Mock Repository From Controller class using Spring Boot and JUnit使用 Spring Boot 和 JUnit 从 Controller 类模拟存储库
【发布时间】:2017-12-20 22:19:46
【问题描述】:

我正在为我的控制器类编写测试用例,它是一个 Spring Boot 应用程序,我只想为调用服务和服务到存储库的控制器类编写测试用例。我正在使用 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();
    }
}

当我运行测试用例时,它没有使用模拟存储库,而是使用 Service 类中提到的@Autowired 存储库。

谁能解释一下如何从控制器类模拟存储库。

在此博客中发布了这么多问题,但没有得到任何回复。

【问题讨论】:

  • 你能告诉我们MyControllertest的类级别注释吗?例如。 @RunWith(SpringJUnit4ClassRunner.class).

标签: junit mocking mockito powermock spring-boot-test


【解决方案1】:

它没有使用您的 Mocks,因为您没有将这些模拟注入到您的控制器/服务类中。相反,您正在自动装配它。
正确的做法是

@RunWith(MockitoJUnitRunner.class)
public class MyControllerTest {

    @InjectMocks
    MyController controller;
    .....

}

更好的解决方案是摆脱字段注入并使用构造函数注入

例如在您的 Controller 和 Service 类中。您可以在 Contructor 上使用 @Autowired 而不是在字段上使用它。例如

class MyService {

   private final MyRepository repo;

   @Autowired
   public MyService(final MyRepository repo) {
      this.repo = repo;
   }
}

同样在Controller类中

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);
    }
  ..............
}

Here 是一篇关于字段注入的好文章

【讨论】:

    【解决方案2】:

    这对我有用:

      @MockBean
      private OrderRepository orderRepository;
    
      @SpyBean
      private OrderService orderService;
    
      @InjectMocks
      private OrderController orderController;
    

    【讨论】:

      【解决方案3】:

      不得致电

      MockitoAnnotations.initMocks(this);
      

      setUp 方法中,因为SpringRunner 已经初始化了模拟。通过调用initMocks,您将一个新对象分配给repoMock。所有像 when 这样的 Mockito 操作都在新对象上执行,但 Spring 仍然使用由 SpringRunner 创建的“旧”MyRepository

      【讨论】:

      • 我正在使用 MockitoAnnotations.initMocks(this);在我的设置方法中。我仍然无法在我的服务类中获取 MockRepository 仍然使用@Autowired
      • 不得使用initMocks
      【解决方案4】:

      解决这个问题的三种方法:

      1.One 和 Mockito 在一起。

       @Mock
       MyRepository repoMock;
       @InjectMocks
       MyService service;
      

      它的作用是按 Class 类型匹配 MyService 类字段,并将 Mocked 变量分配给这些字段。因此,它将模拟注入您的服务。这与 Spring 无关。

      2.重构 MyService 构造函数,使依赖可以通过构造函数注入。 @Autowired 也适用于构造函数,因此它是首选方式。

      @Autowired
      public MyService(MyRespoitory repository){
        this.repository = repository;
      }
      

      3.使用Springs测试环境:

      https://docs.spring.io/spring-security/site/docs/current/reference/html/test-mockmvc.html

      并定义一个 bean,它是您的存储库的模拟版本

      @Bean
      public MyRepository mockMyRepository(){
        return mock(MyRespository.class);
      }
      

      这对于服务测试来说可能会很慢而且很烦人。

      第二种是最首选和最简单的方法。

      【讨论】:

        【解决方案5】:

        这很好用:

        @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));
        }
        

        【讨论】:

        • 请提供更多关于您的程序的描述,以及可能的问题描述。如何修复它。当您的帖子缺少描述时,它对大多数人来说毫无用处
        • 听从@DanielHornik 的指示!
        猜你喜欢
        • 2018-03-09
        • 2022-12-11
        • 2019-08-26
        • 2018-10-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-05-26
        相关资源
        最近更新 更多