Внедрить макет в Spring MockMvc WebApplicationContext
Я работаю над тестированием (через JUnit4 и Spring MockMvc) адаптера службы REST с помощью Spring-boot. Адаптер просто передает запросы, сделанные ему, другой службе REST (используя пользовательский RestTemplate
) и добавляет дополнительные данные к ответам.
Я хотел бы бежать MockMvc
тесты для выполнения тестов интеграции контроллера, но хотите переопределить RestTemplate
в контроллере с макетом, чтобы позволить мне предопределить ответ 3rd party REST и предотвратить его попадание во время каждое испытание. Я смог сделать это, создав экземпляр a MockMvcBuilders.standAloneSetup()
и передавая ему контроллер для тестирования с макетом, введенным, как указано в этом в должности (и моя настройка ниже), однако я не могу сделать то же самое с помощью MockMvcBuilders.webAppContextSetup()
.
Я прошел через несколько других постов, ни один из которых ответа на вопрос о том, как это может быть достигнуто. Я хотел бы использовать фактический контекст приложения Spring для тестов вместо автономного, чтобы предотвратить любые пробелы как приложение, вероятно, будет расти.
EDIT: я использую Mockito в качестве моей насмешливой структуры и пытаюсь ввести один из его насмешек в контекст. Если в этом нет необходимости, тем лучше.
:@RestController
@RequestMapping(Constants.REQUEST_MAPPING_PATH)
public class Controller{
@Autowired
private DataProvider dp;
@Autowired
private RestTemplate template;
@RequestMapping(value = Constants.REQUEST_MAPPING_RESOURCE, method = RequestMethod.GET)
public Response getResponse(
@RequestParam(required = true) String data,
@RequestParam(required = false, defaultValue = "80") String minScore
) throws Exception {
Response resp = new Response();
// Set the request params from the client request
Map<String, String> parameters = new HashMap<String, String>();
parameters.put(Constants.PARAM_DATA, data);
parameters.put(Constants.PARAM_FORMAT, Constants.PARAMS_FORMAT.JSON);
resp = template.getForObject(Constants.RESTDATAPROVIDER_URL, Response.class, parameters);
if(resp.getError() == null){
resp.filterScoreLessThan(new BigDecimal(minScore));
new DataHandler(dp).populateData(resp.getData());
}
return resp;
}
}
тест класс:
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@SpringApplicationConfiguration(classes = MainSpringBootAdapter.class)
@TestPropertySource("/application-junit.properties")
public class WacControllerTest {
private static String controllerURL = Constants.REQUEST_MAPPING_PATH + Constants.REQUEST_MAPPING_RESOURCE + compressedParams_all;
private static String compressedParams_all = "?data={data}&minScore={minScore}";
@Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
@InjectMocks
private Controller Controller;
@Mock
private RestTemplate rt;
@Value("${file}")
private String file;
@Spy
private DataProvider dp;
@Before
public void setup() throws Exception {
dp = new DataProvider(file);
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
}
@Test
public void testGetResponse() throws Exception {
String[] strings = {"requestData", "100"};
Mockito.when(
rt.getForObject(Mockito.<String> any(), Mockito.<Class<Object>> any(), Mockito.<Map<String, ?>> any()))
.thenReturn(populateTestResponse());
mockMvc.perform(get(controllerURL, strings)
.accept(Constants.APPLICATION_JSON_UTF8))
.andDo(MockMvcResultHandlers.print());
Mockito.verify(rt, Mockito.times(1)).getForObject(Mockito.<String> any(), Mockito.<Class<?>> any(), Mockito.<Map<String, ?>> any());
}
private Response populateTestResponse() {
Response resp = new Response();
resp.setScore(new BigDecimal(100));
resp.setData("Some Data");
return resp;
}
}
2 ответов
весной MockRestServiceServer
- это именно то, что вы ищете.
краткое описание от javadoc класса:
главная точка входа для испытания остальных клиент-стороны. Используется для тестов, которые включают прямое или косвенное (через клиентский код) использование RestTemplate. Предоставляет способ настройки мелкозернистых ожиданий запросов, которые будут выполняться через RestTemplate, и способ определения ответов для отправки обратно, устраняя необходимость в фактическом выполнении сервер.
попробуйте настроить свой тест следующим образом:
@WebAppConfiguration
@ContextConfiguration(classes = {YourSpringConfig.class})
@RunWith(SpringJUnit4ClassRunner.class)
public class ExampleResourceTest {
private MockMvc mockMvc;
private MockRestServiceServer mockRestServiceServer;
@Autowired
private WebApplicationContext wac;
@Autowired
private RestOperations restOperations;
@Before
public void setUp() throws Exception {
mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
mockRestServiceServer = MockRestServiceServer.createServer((RestTemplate) restOperations);
}
@Test
public void testMyApiCall() throws Exception {
// Following line verifies that our code behind /api/my/endpoint made a REST PUT
// with expected parameters to remote service successfully
expectRestCallSuccess();
this.mockMvc.perform(MockMvcRequestBuilders.get("/api/my/endpoint"))
.andExpect(status().isOk());
}
private void expectRestCallSuccess() {
mockRestServiceServer.expect(
requestTo("http://remote.rest.service/api/resource"))
.andExpect(method(PUT))
.andRespond(withSuccess("{\"message\": \"hello\"}", APPLICATION_JSON));
}
}
вот еще одно решение. Проще говоря, он просто создает новый RestTemplate
bean и переопределяет уже зарегистрированный.
таким образом, в то время как он выполняет те же функции, что и @mzc answer, он позволяет мне использовать Mockito для создания ответов и проверки сопоставителей немного проще.
это не более чем пара строк кода, но это также предотвращает от добавления дополнительного кода для преобразования из Response
объект в строку выше mockRestServiceServer.expect().andRespond(<String>)
метод arg.
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@SpringApplicationConfiguration(classes = MainSpringBootAdapter.class)
@TestPropertySource("/application-junit.properties")
public class WacControllerTest {
private static String Controller_URL = Constants.REQUEST_MAPPING_PATH + Constants.REQUEST_MAPPING_RESOURCE + compressedParams_all;
@Configuration
static class Config {
@Bean
@Primary
public RestTemplate restTemplateMock() {
return Mockito.mock(RestTemplate.class);
}
}
@Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
@InjectMocks
private Controller Controller;
@Mock
private RestTemplate rt;
@Value("${file}")
private String file;
@Spy
private DataProvider dp;
@Before
public void setup() throws Exception {
dp = new DataProvider(file);
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
this.rt = (RestTemplate) this.wac.getBean("restTemplateMock");
}
@Test
public void testGetResponse() throws Exception {
String[] strings = {"request", "100"};
//Set the request params from the client request
Map<String, String> parameters = new HashMap<String, String>();
parameters.put(Constants.PARAM_SINGLELINE, strings[0]);
parameters.put(Constants.PARAM_FORMAT, Constants.PARAMS_FORMAT.JSON);
Mockito.when(
rt.getForObject(Mockito.<String> any(), Mockito.<Class<Object>> any(), Mockito.<Map<String, ?>> any()))
.thenReturn(populateTestResponse());
mockMvc.perform(get(Controller_URL, strings)
.accept(Constants.APPLICATION_JSON_UTF8))
.andDo(MockMvcResultHandlers.print());
Mockito.verify(rt, Mockito.times(1)).getForObject(Mockito.<String> any(), Mockito.<Class<?>> any(), Mockito.<Map<String, ?>> any());
}
private Response populateTestResponse() {
Response resp = new Response();
resp.setScore(new BigDecimal(100));
resp.setData("Some Data");
return resp;
}
}