如何在没有服务的情况下模拟ResteTemplate交换

时间:2019-05-22 19:11:06

标签: spring spring-mvc mockito springmockito

我有一个静态类,该类将进入另一个项目,以获取我的应用程序启动时所需的信息,因为该项目具有多个子项目,而其他项目则需要这些信息进行处理,因此我必须使其静态并因为我只希望一个实例存在于跨项目中,所以我将构造函数设为私有,因此当其他项目需要此信息列表时,他们只能使用此类class.getinstance()。getList()来获取所有信息,这将迫使其他项目使用相同的信息列出跨子项目。我确实想尝试将此类放在Web服务项目中,然后可以使用Autowired完成所有操作,但是问题出在其他子项目中,他们不能接受Autowired跨项目,因此它使信息列表成为了我只能在Web服务项目中访问该类,该类位于实用程序项目中,因此在我的Web服务项目中,我有一个用于初始化该类的服务,并且该类仅应初始化一次,因此我给它提供了一个带有获取实例方法,因此Web服务类将在此类中调用配置方法以对其进行初始化,并且还使用restTemplate运行Web服务调用以收集所需的信息,因此该类上方没有服务类,现在我想为了测试该类,我使用springRunner对它进行了一次模拟测试,但似乎没有达到我的模拟数据。

我尝试了几种在google上找到的解决方案,它们似乎都与我的情况稍有不同,它们@InjectMocksof服务类并模拟RestTemplat,但是我的情况它不需要服务类,并且由于其私有构造函数,我可以也不要注入它。

这是我位于Utilities项目中的班级。

public class InfoBook
{
//private constructor and config method to set username password and endpoint etc
protected Info LoadInfo()
  {

    final RestTemplate restTemplate = new RestTemplate();

    final HttpHeaders headers = new HttpHeaders();
    headers.setBasicAuth(username, password);
    final HttpEntity<String> request = new HttpEntity<>(headers);

    ResponseEntity<List<InfoResource>> response = null;
    try
    {
      response = restTemplate.exchange(wsEndPoint,
          HttpMethod.GET, request, new ParameterizedTypeReference<List<InfoResource>>()
          {
          });
    }
    catch (final RestClientException e)
    {
      //Catch Exception if anything happened during make the rest request, such as connection refused etc.

    }

    Info info = null;
    if (response != null)
    {
      final List<InfoResource> informationList = response.getBody();
      info = InformationMapper.INSTANCE.inforResourceToInfo(informationList.get(0));
    }

    return info ;
  }
}

这是我进行的测试:

@RunWith(SpringRunner.class)
public class InfoBookTest
{
  @Mock
  private RestTemplate restTemplate;

  @Before
  public void setUp()
  {
InfoBook.configInstance("username", "password", "http://localhost:8080");
     List<InfoResource> informationList = new ArrayList<>();
     InfoResource infoResource = new InfoResource();
     // Set content
     informationList.add(infoResource);

     ResponseEntity<List<InfoResource>> response = new ResponseEntity<>(informationList, HttpStatus.OK);

     Mockito.when(restTemplate.exchange(ArgumentMatchers.any(URI.class),
        ArgumentMatchers.any(HttpMethod.class), ArgumentMatchers.<HttpEntity<String>> any(),
        ArgumentMatchers.<Class<List<InfoResource>>> any())).thenReturn(response);
  }

  @Test
  public void testloadInfo()
  {
    final Info info=
        InfoBook.getInstance().loadInfo();
    Assert.assertEquals(1000, info.getInfoId());
  }
}

现在,如果我运行了此测试,它将尝试对localhost:8080进行Web服务调用,并且当然会得到连接拒绝错误。它似乎并没有击中我的Mockito,然后返回。 有人可以告诉我如何嘲笑吗?

谢谢

2 个答案:

答案 0 :(得分:0)

您的课程有一个RestTemplate的最终字段,它使用一个新的真实RestTemplate进行初始化。实际上,您没有接缝可进行测试,因此目前尚不可能。您将需要在InfoBook中添加一个字段和设置器,可以从测试中调用它,或者更好的是使InfoBook成为服务,并使用bean和自动装配的字段。 InfoBook不再是一个单例,但是Spring无论如何都只会创建一个,因此它实际上是一个单例,只是没有static getInstance(),需要在使用的所有地方进行自动装配。

@Service
public class InfoBook {
    @Autowired
    private RestTemplate restTemplate;

    //private constructor and config method to set username password and endpoint etc
    protected Info LoadInfo() {

        final HttpHeaders headers = new HttpHeaders();
        headers.setBasicAuth(username, password);
        final HttpEntity<String> request = new HttpEntity<>(headers);

        ResponseEntity<List<InfoResource>> response = null;
        try {
            response = restTemplate.exchange(wsEndPoint,
                    HttpMethod.GET, request, new ParameterizedTypeReference<List<InfoResource>>() {
                    });
        } catch (final RestClientException e) {
            //Catch Exception if anything happened during make the rest request, such as connection refused etc.

        }

        Info info = null;
        if (response != null) {
            final List<InfoResource> informationList = response.getBody();
            info = InformationMapper.INSTANCE.inforResourceToInfo(informationList.get(0));
        }

        return info;
    }
}

您需要一个配置类来创建RestTemplate bean

@Configuration
public class RestTemplateConfig {

    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder){
        return restTemplateBuilder.build();
    }
}

最后,您可以仅使用Mockito运行程序进行测试,并且需要使用@InjectMocks创建InfoBook。

@RunWith(MockitoJUnitRunner.class)
public class InfoBookTest
{
  @InjectMocks
  private InfoBook infoBook;
  @Mock
  private RestTemplate restTemplate;
...

答案 1 :(得分:0)

好吧,这是非常愚蠢的,我认为我最好只是回答自己的问题,以便处于相同或相似情况的某人可以获得一些帮助,我如何解决我的问题是我应该将InfoBook类中的restTemplate移到方法之外并给出它是一个get和set方法,在我的测试中,我应该只设置我的模拟restTemplate,然后一切都将被正确模拟,所以不是我使用真正的restTemplate,而是应该使用我的模拟对象,这就是为什么它们不同而我模拟的原因数据未返回。

感谢所有提供帮助的人。