首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >弹簧测试的MockMvc和Spring的HttpInvoker

弹簧测试的MockMvc和Spring的HttpInvoker
EN

Stack Overflow用户
提问于 2016-08-31 19:27:19
回答 1查看 1.1K关注 0票数 1

我有一个web应用程序,它分为两个部分(在不同的jvm中运行):

  • @RestController层;
  • @Service层(业务和数据访问逻辑)。

它们通过Spring相互通信:

代码语言:javascript
复制
(org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean 

@RestController层和

代码语言:javascript
复制
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 )来进行这样的测试?

提前谢谢。

安德烈。

EN

回答 1

Stack Overflow用户

发布于 2017-11-12 23:23:28

好吧,让我谈谈我对这件事的想法。以下是域和api:

代码语言:javascript
复制
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)和“被调用方”上下文(包含真正的服务,而不是远程代理)来测试这样的配置非常简单。就像monolith应用程序上下文一样。快速而简单的接近。但是在某些情况下还不够(例如,您已经为某些目的定制了HttpInvokerProxyFactoryBean,而在另一端定制了HttpInvokerServiceExporter )。
  2. 您可以重写HttpInvokerProxyFactoryBean类,使其成为NonRemote。

首先,通过重写它的一些方法来修改HttpInvokerServiceExporter;它只需要使连接到RemoteInvocation和RemoteInvocationResult的方法成为公共的。

代码语言:javascript
复制
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元素):

代码语言:javascript
复制
<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>

和导入描述符:

代码语言:javascript
复制
<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());

代码语言:javascript
复制
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相同;也用于覆盖)。

代码语言:javascript
复制
<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是

代码语言:javascript
复制
<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类,并将测试上下文描述符导入其中。

代码语言:javascript
复制
@Configuration
@ImportResource(locations = {
        "classpath:openedServiceExporter.xml"
})
static class TunedBusinessConfig {

}

为“调用侧”创建测试配置。我也是这样做的。

代码语言:javascript
复制
@Configuration
@ImportResource(locations = {
        "classpath:nonRemoteInvokerContext.xml",
})
static class TunedRemoteInvokerConfig {

}

现在是考试课。它将通过@WebAppConfiguration进行标记,并具有这样的@ContextHierarchy,这将允许“调用侧”上下文使用“被调用方”上下文(被调用的父、调用-子上下文)。将OpenedHttpServiceExporter注入NonRemoteInvoker是必需的。

代码语言:javascript
复制
@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

我不知道这种做法是否正确,所以在适当的时候才会把答案标记为已接受。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/39257554

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档