Mockito单元测试RestTemplate

问题描述 投票:4回答:4

我正在使用RestTemplate postForEntity方法将正文发布到端点。我需要使用Mockito编写代码测试用例的帮助。返回类型为void,但是如果需要测试,可以将其更改为Typescode。我引用了许多其他文档,但是它们非常笼统,我尝试使用它们,但是由于request和返回类型不同,大多数文档对我来说都不起作用。 。任何建议表示赞赏。谢谢

这是我的Java课堂

    public void postJson(Set<Type> Types){
        try {
            String oneString = String.join(",", Types);
           Map<String, String> requestBody = new HashMap<>();
            requestBody.put("type", oneString);
            requestBody.put("data", "aws");
            JSONObject jsonObject = new JSONObject(requestBody);
            HttpEntity<String> request = new HttpEntity<String>(jsonObject.toString(), null);
            MappingJackson2HttpMessageConverter jsonConvertor = new MappingJackson2HttpMessageConverter();
            restTemplate.getMessageConverters().add(jsonConvertor);
            StringHttpMessageConverter stringConvertor = new StringHttpMessageConverter();
            restTemplate.getMessageConverters().add(stringConvertor);

            int code = (int) result.getcodeValue();

ResponseEntity result = restTemplate.postForEntity(url, new HttpEntity<>(request, getHttpHeaders()), String.class);

        } 
    }
  private HttpEntity getHttpEntity() {
        return new HttpEntity<>(null, getHttpHeaders());
    }

    public HttpHeaders getHttpHeaders() {
        return headersBuilder.build();
    }
} 
java unit-testing post junit resttemplate
4个回答
0
投票

前一段时间,我写了关于unit testing and test doubles的文章。您可以阅读如何进行单元测试的起点。

其中一些关键要点是:

  • 测试行为未实现。与实现细节无关的测试更易于维护。在大多数情况下,测试应着重于测试代码的公共API,并且无需对代码的实现细节进行测试。
  • 受测单元的大小是可自行决定的,但单元越小越好。
  • 谈论单元测试时,另一个典型的区别是被测单元应该是社交的还是单独的。
  • 替身替身是可以在测试中代表真实对象的对象,类似于特技替身替身电影中的演员的方式。他们是测试双打而不是模拟。模拟是测试双打之一。不同的测试双打有不同的用途。

0
投票

由于缺少许多信息,因此很难编写整个测试。例如。什么是Type。由于您没有发布课程的名称,因此暂时将其命名为MyClass。我也假设RestTemplate是通过类似

的构造函数注入的
MyClass(RestTemplate restTemplate) {
    this.restTemplate = restTemplate;
}

以下是使用JUnit 5Mockito进行单元测试的草案。我建议嘲笑RestTemplate。我必须承认,这样一来,我们将不会涵盖测试测试中MappingJackson2HttpMessageConverterStringHttpMessageConverter的用法。

所以非常原始的草稿可能看起来像这样

import java.util.ArrayList;
import java.util.Set;

import org.junit.Assert;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
import org.springframework.http.HttpEntity;
import org.springframework.web.client.RestTemplate;

class MyClassTest {

    private RestTemplate restTemplate;
    private MyClass myClass;

    @BeforeEach
    void setUp() {
        restTemplate = Mockito.mock(RestTemplate.class);
        myClass = new MyClass(restTemplate);
    }

    @Test
    void callMethod() {
        Set<Type> types = Set.of(/* one of your Types */);
        String url = "http://someUrl";
        String httpResult = "";

        Mockito.when(restTemplate.getMessageConverters()).thenReturn(new ArrayList<>());

        ArgumentCaptor<HttpEntity> request = ArgumentCaptor.forClass(HttpEntity.class);
        Mockito.when(restTemplate.postForObject(url, request.capture(), String.class)).thenReturn(httpResult);

        myClass.callMethod(types, url);

        HttpEntity<String> actualHttpEntity = request.getValue();
        Assert.assertEquals(actualHttpEntity.getBody(), "");
    }
}

0
投票

问题中的代码违反了Single responsibility principle。作为结果之一,很难用单元测试来覆盖它。

如果很难编写测试,则代码设计不好。

问题中的班级可以重构为3个班级,职责更少。

  • java.util.function.Predicate方法中的java.util.function.Predicate的[filter
  • [checkType使用TypeChecker方法注入了checkType
  • [PredicatePlanRepo方法注入postJsonTypeChecker的方法

重构示例:

RestTemplate

通过这种方式,可以使用@Component public class TypePredicate implements Predicate<Type> { boolean test(Type mark) { return a.getValue().equals(mark) || b.getValue().equals(mark) || c.getValue().equals(mark); } } @Component public class TypeChecker { private final TypePredicate typePredicate; /* constructor */ public Set<String> checkType(Set<Type> types) { return types.stream() .filter(typePredicate) //... .collect(Collectors.toSet()); } } @Component public class PlanRepo { private final TypeChecker typeChecker; private final RestTemplate restTemplate; private final String url; /* constructor */ public Set<String> postJson(Set<Type> types) { Set<Type> finalValue = typeChecker.checkTypes(types); //... //restTemplate.postForObject(url, request, String.class); //... } } 轻松创建单元测试。

Mockito

此单元测试将涵盖谓词的所有情况,因此其他类的测试无需检查它。只是嘲笑谓词。

@RunWith(MockitoJUnitRunner.class)
public class TypePredicateTest {

  @Mock
  private Type mockType;

  private final TypePredicate typePredicate = new TypePredicate();

  @Test
  public void testSomething() {
    when(mockType.getValue()).thenReturn("something"); //define the behaviour for the mock 
    assertThat(typePredicate.test(mockType)).isTrue(); //AssertJ fluent assertions for Java
  }

  @Test
  public void testSomethingElse() {
    when(mockType.getValue()).thenReturn("something else");    
    assertThat(typePredicate.test(mockType)).isFalse();
  }
}

@RunWith(MockitoJUnitRunner.class) public class TypeCheckerTest { @Mock private TypePredicate typePredicate; @InjectMocks private TypeChecker typeChecker; @Test public void test() { Set<Type> types = Set.of(/*...*/); assertThat(typeChecker.checkType(types)).contains(/*type1, type2, ...*/); verify(typePredicate, times(3)).test(any(Type.class)); //verify that the mock was executed desired number of times } } 相同。可以对其进行模拟和验证。

RestTemplate

以下幻灯片可能有用@RunWith(MockitoJUnitRunner.class) public class PlanRepoTest { @Mock private TypeChecker typeChecker; @Mock private RestTemplate restTemplate; private PlanRepo planRepo; @Before public void setUp() { planRepo = new PlanRepo(typeChecker, restTemplate, "http://example.com"); } @Test public void test() { when(typeChecker.checkType(any(Set.class))).thenReturn(Set.of(/*...*/)); //... //assertThat(planRepo.postJson(...)).contains(...) //verify(restTemplate).postForObject(eq("http://example.com"), ...) } }


0
投票

这是我针对此问题的解决方案,但需要进行一些更改。

首先,您必须将https://www.slideshare.net/iwish1hadanose/test-driven-development-using-mockito外部化为bean,并在其初始化中添加转换器。这样,您将摆脱不涵盖那些转换器的问题。

RestTemplate

这里是包含@Bean public RestTemplate restTemplate() { RestTemplate restTemplate = new RestTemplate(); MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter(); restTemplate.getMessageConverters().add(jsonConverter); StringHttpMessageConverter stringConverter = new StringHttpMessageConverter(); restTemplate.getMessageConverters().add(stringConverter); return restTemplate; } 方法的新类。如您所见,postJsonurl是通过构造函数注入的。这样我们可以测试不同的情况。

restTemplate

这是该方法的测试类。

public class Poster {

    private RestTemplate restTemplate;
    private String url;

    public Poster(RestTemplate restTemplate, String url) {
        this.restTemplate = restTemplate;
        this.url = url;
    }

    public void postJson(Set<Type> types) {
        try {
            String oneString = types.stream().map(Type::toString).collect(Collectors.joining(","));
            Map<String, String> requestBody = new HashMap<>();
            requestBody.put("type", oneString);
            requestBody.put("data", "aws");
            JSONObject jsonObject = new JSONObject(requestBody);
            HttpEntity<String> request = new HttpEntity<>(jsonObject.toString(), null);

            ResponseEntity<String> result = restTemplate
                    .postForEntity(url, new HttpEntity<>(request, getHttpHeaders()), String.class);

            int code = result.getStatusCodeValue();
        } catch (Exception ignored) {}
    }

    private HttpHeaders getHttpHeaders() {
        return new HttpHeaders();
    }

}

最好具有class PosterTest { @Mock private RestTemplate restTemplate; private String url; private Poster poster; @BeforeEach public void setUp() { MockitoAnnotations.initMocks(this); this.url = "http://example.com/posturl"; this.poster = new Poster(restTemplate, url); } @Test void postJson() { // set input, I used TreeSet just to have a sorted set // so that I can check the results against Set<Type> types = new TreeSet<>(Comparator.comparing(Type::toString)); types.add(new Type("a")); types.add(new Type("b")); types.add(new Type("c")); types.add(new Type("d")); // response entity ResponseEntity<String> response = ResponseEntity.ok("RESPONSE"); // mockito mock Mockito.when(restTemplate.postForEntity( ArgumentMatchers.eq(url), ArgumentMatchers.any(HttpHeaders.class), ArgumentMatchers.eq(String.class) )).thenReturn(response); // to check the results Map<String, String> requestBody = new HashMap<>(); requestBody.put("type", "a,b,c,d"); requestBody.put("data", "aws"); JSONObject jsonObject = new JSONObject(requestBody); HttpEntity<String> request = new HttpEntity<>(jsonObject.toString(), null); HttpEntity<HttpEntity<String>> httpEntity = new HttpEntity<>(request, new HttpHeaders()); // actual call poster.postJson(types); // verification Mockito.verify(restTemplate, times(1)).postForEntity( ArgumentMatchers.eq(url), ArgumentMatchers.eq(httpEntity), ArgumentMatchers.eq(String.class)); } } 或其他RestTemplate之类的依赖项,以便可以轻松地对其进行模拟和测试。

© www.soinside.com 2019 - 2024. All rights reserved.