我有一个Spring Bean,它使用RestTemplateBuilder
在其构造函数中发送请求。在测试期间,我想使用MockRestServiceServer
(或者可能模拟用于发送请求的RestTemplateBuilder
)使用一些预定义数据来模拟响应,以便加载应用程序上下文。地址写在application.properties
文件中。
我试过这样做:
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.autoconfigure.web.client.AutoConfigureMockRestServiceServer;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.client.MockRestServiceServer;
import org.springframework.test.web.client.match.MockRestRequestMatchers;
import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
@RunWith(SpringRunner.class)
@AutoConfigureMockRestServiceServer
@SpringBootTest
public class CollectorApplicationTest {
@Autowired
MockRestServiceServer server;
@Value("${components.web-admin-portal.rest.schemas}")
String webAdminPortal;
@Before
public void init() {
server.expect(MockRestRequestMatchers.requestTo(webAdminPortal))
.andRespond(withSuccess("{}", MediaType.APPLICATION_JSON));
}
@Test
public void contextLoads() {
}
}
但是上下文在@Before
方法执行之前加载,并且失败并显示MockRestServiceServer
没有预料到请求的消息。
然后我尝试使用ApplicationContextInitializer
:
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.http.MediaType;
import org.springframework.test.web.client.MockRestServiceServer;
import org.springframework.test.web.client.match.MockRestRequestMatchers;
import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
public class AppInit implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(final ConfigurableApplicationContext context) {
context.refresh();
MockRestServiceServer server = context.getBean(MockRestServiceServer.class);
String webAdminPortal = context.getEnvironment()
.getProperty("components.web-admin-portal.rest.schemas");
server.expect(MockRestRequestMatchers.requestTo(webAdminPortal))
.andRespond(withSuccess("{}", MediaType.APPLICATION_JSON));
}
}
然后它抱怨MockServerRestTemplateCustomizer has not been bound to a RestTemplate
。我认为可以通过在测试类上使用@RestClientTest
注释来解决此问题,因为它会禁用RestTemplateBuilder
的自动配置并启用MockRestServiceServer
的确认:
@RunWith(SpringRunner.class)
@SpringBootTest
@RestClientTest
@ContextConfiguration(initializers = AppInit.class)
public class CollectorApplicationTest {
但它没有改变任何东西。
提前致谢。
答案 0 :(得分:2)
此设置适用于我:
@Service
public class MyService implements ApplicationListener<ContextRefreshedEvent> {
private final RestTemplate restTemplate;
public MyService(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
@Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
//make your call here
restTemplate.getForEntity("http://www.localhost:9090", String.class);
}
}
当且仅当弹簧上下文完全初始化时才会调用
并在测试类中:
@TestConfiguration
public class MockServiceCallConfiguration {
@Autowired
private RestTemplate restTemplate;
@Bean
public MockRestServiceServer mockRestServiceServer() {
MockRestServiceServer server = MockRestServiceServer.createServer(restTemplate);
server.expect(MockRestRequestMatchers.requestTo("http://www.localhost:9090"))
.andRespond(withSuccess("{}", MediaType.APPLICATION_JSON));
return server;
}
}
@SpringBootTest(classes = {MockServiceCallConfiguration.class})
@RunWith(SpringRunner.class)
public class MyServiceTest {
@Test
public void test() {
}
}
这非常繁琐,如果您可以重构代码并避免在初始化设置期间调用休息服务,而是在某些业务事件中,它肯定会更好
答案 1 :(得分:0)
我找到了一种方法(如果有人有更好的解决方案,请留下另一个答案)。 @RestClientTest
执行此操作非常接近本案所需但尚不够。我们需要RestTemplateBuilder
始终生成相同的RestTemplate
,我们还需要绑定到MockRestServiceServer
的{{1}}。我通过用模拟替换RestTemplate
Bean定义并在bean定义中预先配置了RestTemplateBuilder
权限来确保在发送任何请求之前执行此代码。
MockRestServiceServer
应用程序启动成功。
答案 2 :(得分:0)
看看我在 2021 年提出的旧问题,我现在会做不同的事情。
看来我需要从这样的远程服务器获取一些设置(下面的伪代码):
@Service
class MyService {
MySettingsFromRemote settings;
MyService(RestTemplateBuilder builder, @Value("${my-url}") String url){
var rt = builder.build();
setting = rt.getForEntity(url, MySettingsFromRemote.class);
}
...
}
因此,我不会这样做,而是像这样重新组织代码:
@Service
class MyService {
MySettingsFromRemote settings;
MyService(MySettingsFromRemote settings){
this.settings = settings;
}
...
}
@Configuration
class MyConfig {
// maybe Spring provides this bean now?
// rest template is kinda outdated already, webclient is preferred.
@Bean RestTemplate myRestTemplate(RestTemplateBuilder builder) {
return builder.build();
}
@Bean MySettingsFromRemote mySettingsFromRemote(RestTemplate restTemplate, @Value("${my-url}") String url){
return restTemplate.getForEntity(url, MySettingsFromRemote.class);
}
}
然后就很容易测试了。