我的网络应用程序分为两部分 (在不同的jvm中运行):
- @RestController层;
- @Service 层(业务和数据访问逻辑)。
他们通过 Spring Remoting 相互通信:
(org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean
在@RestController层和
org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter
在@Service层上)。
这两部分部署在不同的应用服务器上。 大多数情况下,它们是通过 Spring RestTemplate 进行测试的 (@Service部分必须手动部署和启动 然后运行集成测试)。
但是当我过去使用 Spring Test 和 MockMvc 时,发现它是一个很棒的工具 我想一次又一次地使用。 不幸的是,我不明白如何将 @Service 层上下文添加到测试上下文配置中, 它可以从测试中访问(它保存 @RestController 上下文,并用一些模拟进行了增强)。
如果我使用 @Service 层工件(在本地主机上)手动启动应用程序服务器并运行我的 MockMvc 驱动的测试,我可以看到远程请求 从 MockMvc 到达目的地 - @Service 层 (当然是通过 httpInvoker)。
我想找到在测试上下文中启动 @Service 层上下文的可能性(以及所有需要的 HttpInvokerServiceExporters)。 并强制 httpInvoker 将其请求发送到这个“伪”远程服务(实际上是本地服务)。
现在我正在考虑使用嵌入式jetty来部署@Service层 并针对该实例运行 MockMvc 测试。 SpringHttpRemoting With EmbeddedJettyServer.wiki
我对微服务架构的经验非常少 但看来我的情况对于它来说是相当平常的。 所以也许还有一些更自然的 (特别是尽管有 Spring Test 和 MockMvc)此类测试的方法?
提前致谢。
安德烈。
最佳答案
好吧,让我谈谈我对此事的看法。 这是域名和 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);
}
}
通过提供带有“调用端”上下文的测试来测试此类配置相当简单(在我的例子中为restcontroller) 和“被调用方”上下文(包含真实服务,而不是远程代理)。就像整体应用程序上下文一样。 快速而简单的方法。但在某些情况下还不够 (例如,出于某些目的,您一方面定制了 HttpInvokerProxyFactoryBean,另一方面定制了 HttpInvokerServiceExporter)。
您可以重写 HttpInvokerProxyFactoryBean 类,使其成为 NonRemote。
首先,通过重写 HttpInvokerServiceExporter 的一些方法来修改它;只需将连接到 RemoteInitation 和 RemoteInitationResult 的方法设为公共(public)即可。
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 的名称相同 - 需要用它来覆盖其中一个。
测试上下文描述符openServiceExporter.xml(不带beans元素):
<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 字段 Autowiring 到其中。
覆盖 公共(public)对象调用(方法调用方法调用)
通过调用OpenedHttpServiceExporter.invoke(createRemoteIn Vocation(methodInitation),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。它只需重写父类(super class)的一种方法,并将充当从“调用方”上下文到“被调用方”上下文的桥梁。
使用 NonRemoteInvoker 实例创建“调用方”测试上下文描述符 (nonRemoteInvokerContext.xml) (同样,与原始 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 注入(inject) NonRemoteInvoker 需要它。
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextHierarchy({
@ContextConfiguration(classes = {
ContactControllerIntegrationTest.TunedBusinessConfig.class
}),
@ContextConfiguration(classes = {
ContactControllerIntegrationTest.TunedRemoteInvokerConfig.class
})
})
public class ContactControllerIntegrationTest {
.
.
.
}
这种方法使我不仅可以在测试中涵盖休息层和服务层逻辑,还可以涵盖自定义 RemoteInvoker 的逻辑(我们称之为传输逻辑)。
以下是更多详细信息: https://github.com/PmPozitron/TestingRemoting/tree/lightVersion
我不确定这种方法是否正确,因此在适当的时候不会将答案标记为已接受。
关于java - Spring Test 的 MockMvc 和 Spring Remoting 的 HttpInvoker,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39257554/