#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.