написание юнит-тестов для интерфейса с методами без аргументов - PullRequest
1 голос
/ 23 июня 2019

У меня есть интерфейс, у которого есть метод без аргументов

public interface HealthStatus {
    Integer healthCheck();
}�

Реализация такая, как показано ниже

 @Override
    public Integer healthCheck() {
        Integer status = 0;
        try {
            SimpleClientHttpRequestFactory simpleClientHttpRequestFactory = new SimpleClientHttpRequestFactory();
            if (proxyConfigProperties.getEnabled()) {
                Proxy proxy = new Proxy(java.net.Proxy.Type.HTTP, new InetSocketAddress(proxyConfigProperties.getUrl(), proxyConfigProperties.getPort()));
                simpleClientHttpRequestFactory.setProxy(proxy);
            }
            RestTemplate restTemplate = new RestTemplate(new BufferingClientHttpRequestFactory(simpleClientHttpRequestFactory));
            ResponseEntity<String> healthResponse = restTemplate.exchange(eVerifyGovernmentProperties.getUrl(), HttpMethod.GET, null, String.class);
            status = (healthResponse.getStatusCode() == HttpStatus.OK) ? 200 : 202;
        } catch (Throwable th) {
            th.printStackTrace();
        }

        return status;
    }�

Как я могу протестировать этот метод для положительных и отрицательных сценариев.

Редактировать:

Я реорганизовал мой класс, как показано ниже

@Service
@Qualifier("implementation")
public class HealthStatusImpl implements HealthStatus {

    @Autowired
    RestTemplateConfig restTemplateConfig;

    @Autowired
    private EVerifyGovernmentProperties eVerifyGovernmentProperties;

    @Override
    public Integer healthCheck() {
        Integer status = 0;
        try {
            ResponseEntity<String> healthResponse = restTemplateConfig.getRestTemplate().exchange(eVerifyGovernmentProperties.getUrl(), HttpMethod.GET, null, String.class);
            status = (healthResponse.getStatusCode() == HttpStatus.OK) ? 200 : 202;
        } catch (Throwable th) {
            th.printStackTrace();
        }

        return status;
    }

}

Вот класс, который создает экземпляр RestTemplate

@Component
public class RestTemplateConfig {

    @Autowired
    ProxyConfigProperties proxyConfigProperties;

    public RestTemplate getRestTemplate(){
        SimpleClientHttpRequestFactory simpleClientHttpRequestFactory = new SimpleClientHttpRequestFactory();
        if (proxyConfigProperties.getEnabled()) {
            Proxy proxy = new Proxy(java.net.Proxy.Type.HTTP, new InetSocketAddress(proxyConfigProperties.getUrl(), proxyConfigProperties.getPort()));
            simpleClientHttpRequestFactory.setProxy(proxy);
        }
        RestTemplate restTemplate = new RestTemplate(new BufferingClientHttpRequestFactory(simpleClientHttpRequestFactory));
        return restTemplate;
    }
}

Ответы [ 3 ]

1 голос
/ 23 июня 2019

На самом деле это то, что вы хотите смоделировать:

restTemplateConfig.getRestTemplate().exchange(eVerifyGovernmentProperties.getUrl(), HttpMethod.GET, null, String.class);

, но это потребует глубокой насмешки, и это тоже неприятный запах: вам не нужно определять такое утверждение для вызова шаблона rest.Эта ответственность должна быть определена в определенном классе.
Так что перенесите его в метод определенного компонента, например RestTemplateService, который избавит вас от передачи стольких параметров, а также уравновесит лучшую ответственность этого класса за счет сокращенияего зависимости:

ResponseEntity<String> healthResponse =  restTemplateService.getForHealthCheck();

Теперь просто смоделируйте его с помощью Mockito.
Что касается RestTemplateService, вы можете создать свой собственный класс или полагаться на Feign (здесь Spring Feign имеет больше смысла), который включает декларативный отдых клиента через интерфейс.

Это даст:

public class HealthStatusImpl implements HealthStatus {

    private RestTemplateService restTemplateService;

    // favor constructor injection
    public HealthStatusImpl(RestTemplateService restTemplateService){
        this.restTemplateService = restTemplateService;
    }

    @Override
    public Integer healthCheck() {
        Integer status = 0;
        try {
            ResponseEntity<String> healthResponse = restTemplateService.getForHealthCheck();
            status = healthResponse.getStatusCode().is2xxSuccessful() ? 200 : 400;
        } catch (Throwable th) {
            th.printStackTrace();
        }

        return status;
    }
}

Обратите внимание, что Status.is2xxSuccessful(), как правило, лучше, так как возвращает значение true для любого успешного ответа (200, 201 и т. Д.).И если это не удастся, вы хотите вернуть код ответа об ошибке.

Со стороны модульного тестирования вы должны смоделировать эту зависимость и записать ложное поведение в соответствии с вашими сценариями.
Обратите внимание, что в вашем случае вы не хотите загружать весь контекст пружины, но вы хотитевыполнить простой модульный тест, то есть без контейнера.Так что не используйте @SpringBootTest, а только JUnit и Mockito.

Например, с JUnit 5:

@ExtendWith(MockitoExtension.class)
public class HealthStatusImplTest{

    private HealthStatusImpl healthStatusImpl;

    @Mock
    private RestTemplateService restTemplateServiceMock;

    @BeforeEach
    public void beforeEach(){
        healthStatusImpl = new HealthStatusImpl(restTemplateService);
    }

    @Test
    public void healthCheck_when_200_is_returned(){
       Mockito.when(restTemplateServiceMock)
              .getForHealthCheck().thenReturn(new ResponseEntity(HttpStatus.OK));
       assertEquals(200, healthStatusImpl.healthCheck());
    }

    @Test
    public void healthCheck_when_200_is_not_returned(){
       Mockito.when(restTemplateServiceMock)
              .getForHealthCheck().thenReturn(new ResponseEntity(HttpStatus.NOT_FOUND));
       assertEquals(400, healthStatusImpl.healthCheck());
    }

}

Конечно, RestTemplateService также должен быть унитарным тестом, и унитарные тесты не освобождают от написания интеграционных тестов для компонентов более высокого уровня.

0 голосов
/ 23 июня 2019

Я не уверен, что ваш чек (healthResponse.getStatusCode() == HttpStatus.OK) правильный, потому что, если статус не 2xx RestTemplate, выдает HttpStatusCodeException.

Вот почему всегда лучше иметь несколько издевательств, если вы интегрируетесь со сторонней стороной.

Вот почему я также рекомендую учитывать MockRestServiceServer в своих тестах. см. @Puce ответ для ссылок.

Кроме того, нет необходимости создавать новый RestTemplate для каждого запроса.

Вот почему я также рекомендую вам рассмотреть внедрение в конструктор. см. ответ @davidxxx о подходах к рефакторингу. И не забудьте установить время ожидания соединения в настройках RestTemplate.

0 голосов
/ 23 июня 2019

Для модульного тестирования веб-клиента, такого как класс, основанный на RestTemplate, вам нужна среда, которая выполняет макет сервера, например,

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...