我有一个web应用程序,它分为两个部分(在不同的jvm中运行):
它们通过Spring相互通信:
(org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean @RestController层和
org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter 在@Service层)。
这两个部分部署在不同的应用服务器上。大多数情况下,它们都是通过Spring RestTemplate进行测试的(@Service必须手动部署和启动,然后运行集成测试)。
但是当我过去使用Spring和MockMvc时,我发现它是一个很好的工具,我想一次又一次地使用。不幸的是,我不明白如何将@Service层上下文添加到测试上下文配置中,这样就可以从test访问@RestController上下文(其中包含@RestController上下文,并添加了一些模拟)。
如果我使用@Service工件(在本地主机上)手动启动应用服务器,并运行MockMvc驱动的测试,我可以看到来自MockMvc的远程请求到达了它们的目的地- @Service (当然是通过httpInvoker )。
我希望找到在测试上下文中启动@Service层上下文的可能性(使用所有需要的HttpInvokerServiceExporters)。并迫使httpInvoker将其请求发送到这个“伪”远程服务(实际上是本地的)。
现在,我正在考虑使用嵌入式jetty来部署@Service层并针对这个实例运行MockMvc测试。SpringHttpRemoting与EmbeddedJettyServer.wiki
我在MicroService体系结构方面的经验非常有限,但似乎我的情况对它来说是相当平常的。因此,也许还有一些更自然的方法(尤其是Spring和MockMvc )来进行这样的测试?
提前谢谢。
安德烈。
发布于 2017-11-12 23:23:28
好吧,让我谈谈我对这件事的想法。以下是域和api:
public class Contact implements Serializable {
private String firstName;
private String lastName;
private DateTime birthDate;
}
public interface ContactService {
List<String> getContacts();
}
@Service
public class ContactServiceImpl implements ContactService{
public List<String> getContacts() {
return new ArrayList<String>(asList("karl marx", " fridrih engels", " !!!"));
}
}
@RestController
public class ContactController {
public static final String QUALIFIER = "contactController";
public static final String MAPPING = "/contact";
@Autowired
private ContactService serviceInvoker;
@RequestMapping(method = RequestMethod.GET)
public ResponseEntity<String> findAll() {
List<String> strings = serviceInvoker.getContacts();
return new ResponseEntity<String>(Arrays.toString(strings.toArray()), HttpStatus.OK);
}
}首先,通过重写它的一些方法来修改HttpInvokerServiceExporter;它只需要使连接到RemoteInvocation和RemoteInvocationResult的方法成为公共的。
public class OpenedHttpServiceExporter extends HttpInvokerServiceExporter {
@Override
public RemoteInvocation readRemoteInvocation(HttpServletRequest request) throws IOException, ClassNotFoundException {
return super.readRemoteInvocation(request);
}
.
.
.
etc...
}让它成为OpenedHttpServiceExporter。在测试/资源中创建bean描述符,将测试中需要的生产bean定义导入其中,并添加OpenedHttpServiceExporter bean,其名称与原始HttpInvokerServiceExporter具有相同的名称--这是用另一个名称重写一个bean所必需的。
测试上下文描述符openedServiceExporter.xml (没有bean元素):
<import resource="classpath:spring/serviceExporter.xml"/>
<bean id="contactExporter" class="pmp.testingremoting.service.OpenedHttpServiceExporter">
<property name="service" ref="contactServiceImpl"/>
<property name="serviceInterface" value="pmp.testingremoting.service.ContactService"/>
</bean>和导入描述符:
<context:annotation-config/>
<context:component-scan base-package="pmp.testingremoting.service">
<context:exclude-filter type="annotation" expression="org.springframework.context.annotation.Configuration"/>
</context:component-scan>
<bean name="contactExporter" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter">
<property name="service" ref="contactServiceImpl"/>
<property name="serviceInterface" value="pmp.testingremoting.service.ContactService"/>
</bean>扩展HttpInvokerProxyFactoryBean,生成这个子类的bean,将HttpInvokerServiceExporter字段自动导入其中。
覆盖公共对象调用(MethodInvocation methodInvocation)
通过在其中调用OpenedHttpServiceExporter.invoke(createRemoteInvocation(methodInvocation), exporter.getService());。
public class NonRemoteInvoker extends HttpInvokerProxyFactoryBean {
@Autowired
private OpenedHttpServiceExporter exporter;
public void setExporter(OpenedHttpServiceExporter exporter) {
this.exporter = exporter;
}
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
return exporter.invoke(createRemoteInvocation(methodInvocation), exporter.getService());
}
}让我们把这个新的类称为NonRemoteInvoker。它必须只覆盖超类的一种方法,并充当从“调用侧”上下文到“被调用方”上下文的桥梁。
使用nonRemoteInvokerContext.xml实例创建‘调用侧’测试上下文描述符( NonRemoteInvoker )(同样,名称与原始HttpInvokerProxyFactoryBean相同;也用于覆盖)。
<import resource="classpath:spring/webContext.xml"/>
<bean id="serviceInvoker" class="pmp.testingremoting.controller.NonRemoteInvoker">
<property name="serviceUrl" value="http://localhost:8080/remote/ContactService" />
<property name="serviceInterface" value="pmp.testingremoting.service.ContactService" />
</bean>而webContext.xml是
<mvc:annotation-driven/>
<context:annotation-config/>
<context:component-scan base-package="pmp.testingremoting.controller">
<context:exclude-filter type="annotation" expression="org.springframework.context.annotation.Configuration"/>
</context:component-scan>为“被调用的侧”创建测试配置。我使用了静态@Configuration类,并将测试上下文描述符导入其中。
@Configuration
@ImportResource(locations = {
"classpath:openedServiceExporter.xml"
})
static class TunedBusinessConfig {
}为“调用侧”创建测试配置。我也是这样做的。
@Configuration
@ImportResource(locations = {
"classpath:nonRemoteInvokerContext.xml",
})
static class TunedRemoteInvokerConfig {
}现在是考试课。它将通过@WebAppConfiguration进行标记,并具有这样的@ContextHierarchy,这将允许“调用侧”上下文使用“被调用方”上下文(被调用的父、调用-子上下文)。将OpenedHttpServiceExporter注入NonRemoteInvoker是必需的。
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextHierarchy({
@ContextConfiguration(classes = {
ContactControllerIntegrationTest.TunedBusinessConfig.class
}),
@ContextConfiguration(classes = {
ContactControllerIntegrationTest.TunedRemoteInvokerConfig.class
})
})
public class ContactControllerIntegrationTest {
.
.
.
}这种方法允许我在测试中不仅涉及rest和服务层逻辑,还包括自定义RemoteInvoker的逻辑(让我们称之为传输逻辑)。
下面是更多详细信息:https://github.com/PmPozitron/TestingRemoting/tree/lightVersion
我不知道这种做法是否正确,所以在适当的时候才会把答案标记为已接受。
https://stackoverflow.com/questions/39257554
复制相似问题