Метод тестирования с вызовом okhttp3

#java #unit-testing #spring-boot #mocking #okhttp3

#java #модульное тестирование #весенняя загрузка #издевательство #okhttp

Вопрос:

У меня есть метод в классе service, который выполняет вызов внешнего api. Как бы я издевался над этим вызовом OkHttpClient? Я пытался сделать это с помощью mockito, но безуспешно.

 //this is the format of the method that i want to test
public string sendMess(EventObj event) {
    OkHttpClient client = new OkHttpClient();
    //build payload using the information stored in the payload object
    ResponseBody body = 
        RequestBody.create(MediaType.parse("application/json"), payload);
    Request request = //built using the Requestbody
    //trying to mock a response from execute
    Response response = client.newCall(request).execute();
    //other logic
}
  

Я открыт для рефакторинга класса service, если это поможет с тестированием. Приветствуются любые предложения и рекомендации. Спасибо.

Комментарии:

1. Какая логика тестового примера? «Другая логика», о которой я говорю, — это просто форматирование ответа и его возврат.

Ответ №1:

Поскольку вы используете spring-boot , оставьте управляющие компоненты для spring.

1) Сначала создайте OkHttpClient как spring bean, чтобы вы могли использовать его во всем приложении

 @Configuration
public class Config {

@Bean
public OkHttpClient okHttpClient() {
    return new OkHttpClient();
    }
 }
  

2) А затем в классе service @Autowire OkHttpClient и использовать его

 @Service
public class SendMsgService {

@Autowired
private OkHttpClient okHttpClient;

 public string sendMess(EventObj event) {

ResponseBody body =  RequestBody.create(MediaType.parse("application/json"), payload);
Request request = //built using the Requestbody
//trying to mock a response from execute
Response response = okHttpClient.newCall(request).execute();
//other logic
   }
 }
  

Тесты

3) Теперь в тестовых классах используются @SpringBootTest , @RunWith(SpringRunner.class) и @MockBean

Аннотацию @SpringBootTest можно использовать, когда нам нужно загрузить весь контейнер. Аннотация работает путем создания ApplicationContext, который будет использоваться в наших тестах.

@RunWith(SpringRunner.class ) используется для обеспечения моста между функциями тестирования Spring Boot и JUnit. Всякий раз, когда мы используем какие-либо функции тестирования Spring Boot в наших тестах JUnit, эта аннотация будет обязательной.

Аннотация @MockBean, которую можно использовать для добавления макетов в Spring ApplicationContext.

 @SpringBootTest
@RunWith(SpringRunner.class)
public class ServiceTest {

 @Autowire
 private SendMsgService sendMsgService;

 @MockBean
 private OkHttpClient okHttpClient;

  @Test
  public void testSendMsg(){

 given(this.okHttpClient.newCall(ArgumentMatchers.any())
            .execute()).willReturn(String);

  EventObj event = //event object
 String result = sendMsgService.sendMess(event);

  }
 }
  

Ответ №2:

Я бы посоветовал, чтобы вы извлекли экземпляр вашего OkHttpClient собственного метода в Configuration классе. После этого вы можете @Inject установить клиент везде, где это необходимо, и тестирование становится намного проще, потому что вы можете @Mock его удалить.

Так сказать, Spring управляемый компонент:

 @Configuration
public class OkHttpClientConfiguration {
    @Bean
    public OkHttpClient okHttpClient() {
        return new OkHttpClient();
    }
}
  

… ваш производственный класс:

 @Component
public class ProductionClass {
    @Inject
    private OkHttpClient okHttpClient;

    public string sendMess(EventObj event) {
       okHttpClient // whatever you want
       […]
    }
}
  

… и ваш тест:

 public class SpyTest {
    @InjectMocks
    private ProductionClass productionClass;
    @Mock
    private OkHttpClient okHttpClient;


    @Before
    public void initMocks() {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void spyInsteadOfPowermock() {
        Request request = // mock the request
        when(okHttpClient.newCall(request)).thenReturn(mock(Call.class));
    }
}
  

Комментарии:

1. Я пробовал это. Я попытался издеваться над OkHttpClient.newCall (request).execute, это не работает, потому что OkHttpClient.newCall (request) равен нулю, поскольку я не могу по-настоящему издеваться над этим. newCall возвращает объект Call, который затем используется для вызова метода execute() внутри Call.

2. Почему вы не можете издеваться okHttpClient.newCall(request) ? Какой тип имеет ваш request объект? Если вы не можете издеваться request в целом, вы можете издеваться над ним с Matcher подобным: when(okHttpClient.newCall(any(RequestClass.class))).thenReturn(/*whateverResult*/); .

3. Не нужно ли мне издеваться над классом вызова, поскольку OkHttpClient.newCall() возвращает объект вызова?

4. Да, точно, извините, рассмотрите мой RequestClass.class просто в качестве примера, я не настолько знаком с OkHttp API.