Модульное тестирование Mockito RestTemplate - PullRequest
4 голосов
/ 17 января 2020

Я использую метод RestTemplate postForEntity для отправки тела в конечную точку. Мне нужна помощь в написании тестового примера для моего кода с использованием Mockito. Тип возвращаемого значения void, но при необходимости его можно изменить на Types или code. Я ссылался на многие другие документы, но они носят общий характер, я пытался их использовать, но большинство из них не работало для меня, так как request и тип возвращаемого значения разные. , Любые предложения приветствуются. Спасибо

Вот мой Java класс

    public void postJson(Set<Type> Types){
        try {
            String oneString = String.join(",", Types);
           Map<String, String> requestBody = new HashMap<>();
            requestBody.put("type", oneString);
            JSONObject jsonObject = new JSONObject(requestBody);
            HttpEntity<String> request = new HttpEntity<String>(jsonObject.toString(), null);
ResponseEntity result = restTemplate.exchange(url, HttpMethod.POST,
                    new HttpEntity<>(request, getHttpHeaders()), String.class);

        } 
    }
} 

Ответы [ 4 ]

1 голос
/ 27 января 2020

Вы тестируете логику c внутри класса MyClass, поэтому не стоит насмехаться над ней. RestTemplate - это зависимость внутри MyClass, так что это именно то, что вам нужно для насмешки. В целом, в вашем тесте это должно выглядеть так:

Это простой пример. Хорошей практикой будет проверка того, что аргументы, передаваемые на ваш макет, соответствуют ожидаемым. Одним из способов было бы заменить Mockito.eq() реальными ожидаемыми данными. Другой способ проверить это отдельно, например:

public ResponseEntity<String> postJson(Set<Type> Types){
            try {
                String oneString = String.join(",", Types);
               Map<String, String> requestBody = new HashMap<>();
                requestBody.put("type", oneString);
                JSONObject jsonObject = new JSONObject(requestBody);
                HttpEntity<String> request = new HttpEntity<String>(jsonObject.toString(), null);
    ResponseEntity result = restTemplate.exchange(url, HttpMethod.POST,
                        new HttpEntity<>(request, getHttpHeaders()), String.class);
                } 
        }
        return Types;

Вы можете написать тест для вышеуказанного метода следующим образом

       @Mock
       RestTemplate restTemplate;
           private Poster poster;

          HttpEntity<String> request = new HttpEntity<>(jsonObject.toString(), getHttpHeaders());

           ResponseEntity<String> result = restTemplate.exchange(uri, HttpMethod.POST, request, String.class);

           Mockito.verify(restTemplate, Mockito.times(1)).exchange(Mockito.eq(uri), Mockito.eq(HttpMethod.POST),
                   Mockito.eq(request), Mockito.eq(String.class));

           Assert.assertEquals(result, poster.postJson(mockData));
       }

       HttpHeaders getHttpHeaders() {
           HttpHeaders headers = new HttpHeaders();
           headers.add(// whatever you need to add);
           return headers;
       }
   }


0 голосов
/ 21 января 2020

Трудно написать целый тест, так как много информации отсутствует. Например, что такое Type. Поскольку вы не опубликовали название своего класса, я просто назову его MyClass. Также я предполагаю, что RestTemplate внедряется через конструктор, как

MyClass(RestTemplate restTemplate) {
    this.restTemplate = restTemplate;
}

Ниже приведен черновик для модульного теста с использованием JUnit 5 и Mockito . Я бы предложил издеваться над RestTemplate. Я должен признать, что этот способ не будет описан для тестирования использования MappingJackson2HttpMessageConverter и StringHttpMessageConverter в нашем тесте.

Так что очень грубый черновик может выглядеть следующим образом

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 голосов
/ 24 января 2020

Вот мое решение этой проблемы, но оно требует некоторых изменений.

Прежде всего, вы должны экстернализовать 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;
}

Вот новый класс, содержащий метод postJson. Как видите, url и 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 или другие ServiceClass Это так, чтобы их можно было легко проверять и проверять.

0 голосов
/ 17 января 2020

Некоторое время назад я писал о модульном тестировании и двойных тестах . Вы можете прочитать в качестве отправной точки, как подходить к юнит-тестированию.

Вот некоторые из его ключевых выводов:

  • Поведение теста, а не реализация. Тесты, которые не зависят от деталей реализации, легче поддерживать. В большинстве случаев тесты должны быть сосредоточены на тестировании API-интерфейса publi c вашего кода, и детали реализации вашего кода не должны подвергаться тестам.
  • Размер тестируемого модуля является произвольным, но меньшим чем лучше единица, тем лучше.
  • Говоря о модульных тестах, более существенное различие заключается в том, должна ли тестируемая единица быть общительной или одиночной.
  • Двойной тест - это объект, который может заменять реальный объект в тесте, аналогично тому, как дублер заменяет актера в мове ie. Они тестовые двойники, а не издевательства. Макет - один из двойников теста. Разные тестовые пары имеют разное применение.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...