【问题标题】:Spring Test's MockMvc and Spring Remoting's HttpInvokerSpring Test 的 MockMvc 和 Spring Remoting 的 HttpInvoker
【发布时间】:2016-08-31 19:27:19
【问题描述】:

我有一个分为两部分的 Web 应用程序 (在不同的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 将其请求发送到这个“伪”远程服务(实际上是本地服务)。

现在我正在考虑使用嵌入式码头来部署@Service 层 并对这个实例运行 MockMvc 测试。 SpringHttpRemoting With EmbeddedJettyServer.wiki

我在微服务架构方面的经验非常少 但我的情况似乎很正常。 所以也许有一些更自然的 (尽管特别是 Spring Test 和 MockMvc)这种测试的方法?

提前致谢。

安德烈。

【问题讨论】:

    标签: java spring-mvc integration-testing mockmvc httpinvoker


    【解决方案1】:

    好吧,让我谈谈我对此事的看法。 这是域和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,重写它的一些方法;只需要将连接到 RemoteInvocation 和 RemoteInvocationResult 的方法公开。

    public class OpenedHttpServiceExporter extends HttpInvokerServiceExporter {
    
            @Override
            public RemoteInvocation readRemoteInvocation(HttpServletRequest request) throws IOException, ClassNotFoundException {
                return super.readRemoteInvocation(request);
            }
            .
            .
            .
            etc...
        }
    

    让它成为OpenedHttpServiceExporter。在测试/资源中创建 bean 描述符,将测试中需要的生产 bean 定义导入其中 并添加与原始 HttpInvokerServiceExporter 同名的 OpenedHttpServiceExporter bean - 需要它来覆盖另一个。

    测试上下文描述符openedServiceExporter.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 字段插入其中。

    覆盖 公共对象调用(方法调用方法调用)

    致电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。它只需要覆盖超类的一个方法,并将充当从“调用方”上下文到“被调用方”上下文的桥梁。

    使用 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 注入 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

    我不确定这种方法是否正确,所以直到适当的时候才会将答案标记为已接受。

    【讨论】:

      猜你喜欢
      • 2019-12-20
      • 1970-01-01
      • 1970-01-01
      • 2016-04-03
      • 1970-01-01
      • 1970-01-01
      • 2016-02-05
      • 2019-11-02
      • 2016-03-13
      相关资源
      最近更新 更多