java - Spring Test 的 MockMvc 和 Spring Remoting 的 HttpInvoker

标签 java spring-mvc integration-testing mockmvc httpinvoker

我的网络应用程序分为两部分 (在不同的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);
    }
}
  1. 通过提供带有“调用端”上下文的测试来测试此类配置相当简单(在我的例子中为restcontroller) 和“被调用方”上下文(包含真实服务,而不是远程代理)。就像整体应用程序上下文一样。 快速而简单的方法。但在某些情况下还不够 (例如,出于某些目的,您一方面定制了 HttpInvokerProxyFactoryBean,另一方面定制了 HttpInvokerServiceExporter)。

  2. 您可以重写 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/

相关文章:

unit-testing - 集成和单元测试之间的 Grails 污染

java - 使用注释 @DataJpaTest 进行测试 - EmbeddedDatabaseType 是必需的

java - "cannot find the main class"使用 subprocess.Popen 调用 java 程序时出错,而同一个调用只能使用 os.system

java - 我可以使用 <http :auto-config=true> and <UsernamePasswordAuthenticationFilter> at the same time in spring security

java - 获取 java.sql.SQLException : Operation not allowed after ResultSet closed ERROR while trying multiple queries

unit-testing - Jacoco for IntegrationTests 的代码覆盖率报告在 Weblogic 服务器上运行

Java反射错误: NoSuchMethodException

java - 如何使用 jUnit 使用 java 配置类而不使用 XML 来测试 spring mvc 中的 DAO 实现方法

java - 如何创建具有两个映射字段的单独表?

json - Spring Controller 修剪 JSON 数据的建议