使用带有 Spring Boot 的 Spock 测试框架来测试我的 REST 控制器

Posted

技术标签:

【中文标题】使用带有 Spring Boot 的 Spock 测试框架来测试我的 REST 控制器【英文标题】:Using Spock Testing Framework with Spring Boot to test my REST Controller 【发布时间】:2017-06-28 21:24:14 【问题描述】:

情况

我有一个运行简单 REST 服务的 Spring Boot 应用程序。我想测试这项服务。由于我喜欢 Spock 框架,我很想在这里使用它,但我无法解决 Spring 配置的问题。

问题

测试本身会运行,但会引发java.net.ConnectException: Connection refused: connect 异常。这里的问题是我要测试的实际应用程序没有运行 - 因此没有可用的 REST 接口。如何确保在测试运行之前实际应用程序正在运行?使用 JUnit 这个问题有点小,因为 Spring 有它的注释。但是 Spock 呢?

编辑:我尝试使用以下文章:http://henningpetersen.com/post/18/testing-spring-mvc-controllers-with-spock

RestHandler.java

@RestController
public class RestHandler 

  private @Autowired ExecutionService executionService;

  public RestHandler(ExecutionService executionService) 
    this.executionService = executionService;
  

  @RequestMapping(value = "/toolbox/exec", method = POST,
          consumes = MediaType.APPLICATION_JSON_VALUE,
          produces = MediaType.TEXT_PLAIN_VALUE)
  public String executeTool(@RequestBody Tool tool) 
    executionService.execute(tool);
    return "thanks";
  


ToolExecutor.java

@SpringBootApplication
public class ToolExecutor 

    public static void main(String[] args) 
        SpringApplication.run(ToolExecutor.class, args);
    

RestHandlerTest.groovy

@SpringBootTest
class RestHandlerTest extends Specification 

    def someService = Mock(ExecutionService)
    def underTest = new RestHandler(someService);
    def mockMvc = MockMvcBuilders.standaloneSetup(underTest).build()

    @Shared
    def client = new RESTClient("http://localhost:603/toolbox/exec")

    def "ExecuteToolTest"() 
        when: "If POST has valid JSON format"
        Closure object =  "\"name\":\"test\",\"parameters\":[\"abc\",\"def\"]" 
        def response = client.request(Method.POST, object);
        then:
        with(response)
            data.text == "thanks"
            status == 200
        

    

日志

    15:22:07.601 [main] DEBUG org.springframework.core.env.StandardEnvironment - Adding [systemProperties] PropertySource with lowest search precedence
15:22:07.605 [main] DEBUG org.springframework.core.env.StandardEnvironment - Adding [systemEnvironment] PropertySource with lowest search precedence
15:22:07.605 [main] DEBUG org.springframework.core.env.StandardEnvironment - Initialized StandardEnvironment with PropertySources [systemProperties,systemEnvironment]
15:22:07.855 [main] DEBUG org.springframework.test.web.servlet.setup.StandaloneMockMvcBuilder$StaticRequestMappingHandlerMapping - Looking for request mappings in application context: org.springframework.test.web.servlet.setup.StubWebApplicationContext@17f9d882
15:22:07.897 [main] DEBUG org.springframework.test.web.servlet.setup.StandaloneMockMvcBuilder$StaticRequestMappingHandlerMapping - 2 request handler methods found on class com.company.toolbox.components.executor.RestHandler: public void com.company.toolbox.components.executor.RestHandler.isAlive()=[/toolbox/exec],methods=[GET], public java.lang.String com.company.toolbox.components.executor.RestHandler.executeTool(com.company.toolbox.commons.Tool)=[/toolbox/exec],methods=[POST],consumes=[application/json],produces=[text/plain]
15:22:07.900 [main] INFO org.springframework.test.web.servlet.setup.StandaloneMockMvcBuilder$StaticRequestMappingHandlerMapping - Mapped "[/toolbox/exec],methods=[GET]" onto public void com.company.toolbox.components.executor.RestHandler.isAlive()
15:22:07.907 [main] INFO org.springframework.test.web.servlet.setup.StandaloneMockMvcBuilder$StaticRequestMappingHandlerMapping - Mapped "[/toolbox/exec],methods=[POST],consumes=[application/json],produces=[text/plain]" onto public java.lang.String com.company.toolbox.components.executor.RestHandler.executeTool(com.company.toolbox.commons.Tool)
15:22:08.285 [main] DEBUG org.jboss.logging - Logging Provider: org.jboss.logging.Slf4jLoggerProvider
15:22:08.286 [main] INFO org.hibernate.validator.internal.util.Version - HV000001: Hibernate Validator 5.3.4.Final
15:22:08.307 [main] DEBUG org.hibernate.validator.internal.engine.resolver.DefaultTraversableResolver - Cannot find javax.persistence.Persistence on classpath. Assuming non JPA 2 environment. All properties will per default be traversable.
15:22:08.317 [main] DEBUG org.hibernate.validator.internal.engine.ConfigurationImpl - Setting custom MessageInterpolator of type org.springframework.validation.beanvalidation.LocaleContextMessageInterpolator
15:22:08.318 [main] DEBUG org.hibernate.validator.internal.engine.ConfigurationImpl - Setting custom ParameterNameProvider of type com.sun.proxy.$Proxy26
15:22:08.320 [main] DEBUG org.hibernate.validator.internal.xml.ValidationXmlParser - Trying to load META-INF/validation.xml for XML based Validator configuration.
15:22:08.323 [main] DEBUG org.hibernate.validator.internal.xml.ResourceLoaderHelper - Trying to load META-INF/validation.xml via TCCL
15:22:08.324 [main] DEBUG org.hibernate.validator.internal.xml.ResourceLoaderHelper - Trying to load META-INF/validation.xml via Hibernate Validator's class loader
15:22:08.324 [main] DEBUG org.hibernate.validator.internal.xml.ValidationXmlParser - No META-INF/validation.xml found. Using annotation based configuration only.
15:22:08.386 [main] INFO org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter - Looking for @ControllerAdvice: org.springframework.test.web.servlet.setup.StubWebApplicationContext@17f9d882
15:22:08.422 [main] DEBUG org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver - Looking for exception mappings: org.springframework.test.web.servlet.setup.StubWebApplicationContext@17f9d882
15:22:08.442 [main] DEBUG org.springframework.test.web.servlet.TestDispatcherServlet - Initializing servlet ''
15:22:08.452 [main] DEBUG org.springframework.web.context.support.StandardServletEnvironment - Adding [servletConfigInitParams] PropertySource with lowest search precedence
15:22:08.452 [main] DEBUG org.springframework.web.context.support.StandardServletEnvironment - Adding [servletContextInitParams] PropertySource with lowest search precedence
15:22:08.455 [main] DEBUG org.springframework.web.context.support.StandardServletEnvironment - Adding [systemProperties] PropertySource with lowest search precedence
15:22:08.455 [main] DEBUG org.springframework.web.context.support.StandardServletEnvironment - Adding [systemEnvironment] PropertySource with lowest search precedence
15:22:08.455 [main] DEBUG org.springframework.web.context.support.StandardServletEnvironment - Initialized StandardServletEnvironment with PropertySources [servletConfigInitParams,servletContextInitParams,systemProperties,systemEnvironment]
15:22:08.455 [main] INFO org.springframework.mock.web.MockServletContext - Initializing Spring FrameworkServlet ''
15:22:08.455 [main] INFO org.springframework.test.web.servlet.TestDispatcherServlet - FrameworkServlet '': initialization started
15:22:08.457 [main] DEBUG org.springframework.test.web.servlet.TestDispatcherServlet - Unable to locate MultipartResolver with name 'multipartResolver': no multipart request handling provided
15:22:08.458 [main] DEBUG org.springframework.test.web.servlet.TestDispatcherServlet - Using LocaleResolver [org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver@267f474e]
15:22:08.458 [main] DEBUG org.springframework.test.web.servlet.TestDispatcherServlet - Using ThemeResolver [org.springframework.web.servlet.theme.FixedThemeResolver@7a7471ce]
15:22:08.458 [main] DEBUG org.springframework.test.web.servlet.TestDispatcherServlet - Using RequestToViewNameTranslator [org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator@28276e50]
15:22:08.458 [main] DEBUG org.springframework.test.web.servlet.TestDispatcherServlet - Using FlashMapManager [org.springframework.web.servlet.support.SessionFlashMapManager@62e70ea3]
15:22:08.458 [main] DEBUG org.springframework.test.web.servlet.TestDispatcherServlet - Published WebApplicationContext of servlet '' as ServletContext attribute with name [org.springframework.web.servlet.FrameworkServlet.CONTEXT.]
15:22:08.458 [main] INFO org.springframework.test.web.servlet.TestDispatcherServlet - FrameworkServlet '': initialization completed in 3 ms
15:22:08.458 [main] DEBUG org.springframework.test.web.servlet.TestDispatcherServlet - Servlet '' configured successfully
15:22:08.476 [main] DEBUG groovyx.net.http.RESTClient - POST http://localhost:603/toolbox/exec
15:22:08.728 [main] DEBUG org.apache.http.impl.conn.BasicClientConnectionManager - Get connection for route ->http://localhost:603
15:22:08.742 [main] DEBUG org.apache.http.impl.conn.DefaultClientConnectionOperator - Connecting to localhost:603
15:22:09.744 [main] DEBUG org.apache.http.impl.conn.DefaultClientConnectionOperator - Connect to localhost:603 timed out. Connection will be retried using another IP address
15:22:09.745 [main] DEBUG org.apache.http.impl.conn.DefaultClientConnectionOperator - Connecting to localhost:603
15:22:10.745 [main] DEBUG org.apache.http.impl.conn.DefaultClientConnection - Connection org.apache.http.impl.conn.DefaultClientConnection@21362712 closed
15:22:10.745 [main] DEBUG org.apache.http.impl.conn.DefaultClientConnection - Connection org.apache.http.impl.conn.DefaultClientConnection@21362712 shut down
15:22:10.746 [main] DEBUG org.apache.http.impl.conn.BasicClientConnectionManager - Releasing connection org.apache.http.impl.conn.ManagedClientConnectionImpl@27eb3298

例外

java.net.ConnectException: Connection refused: connect

    at java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:79)
    at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
    at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
    at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
    at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172)
    at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
    at java.net.Socket.connect(Socket.java:589)
    at org.apache.http.conn.scheme.PlainSocketFactory.connectSocket(PlainSocketFactory.java:120)
    at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:179)
    at org.apache.http.impl.conn.ManagedClientConnectionImpl.open(ManagedClientConnectionImpl.java:328)
    at org.apache.http.impl.client.DefaultRequestDirector.tryConnect(DefaultRequestDirector.java:612)
    at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:447)
    at org.apache.http.impl.client.AbstractHttpClient.doExecute(AbstractHttpClient.java:884)
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82)
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:55)
    at groovyx.net.http.HTTPBuilder.doRequest(HTTPBuilder.java:476)
    at groovyx.net.http.HTTPBuilder.doRequest(HTTPBuilder.java:441)
    at groovyx.net.http.HTTPBuilder.request(HTTPBuilder.java:373)
    at com.xetra11.toolbox.components.executor.RestHandlerTest.ExecuteToolTest(RestHandlerTest.groovy:26)


Process finished with exit code -1

【问题讨论】:

检查日志。 Spring Boot 应用在测试中启动的端口是什么? 我已经尝试将端口更改为 8080,因为我认为这个 TestServer 无法通过属性知道我的自定义端口设置。还是没有运气。然后我试图通过这个问题找出实际的端口:***.com/questions/30312058/… - 也没有运气 - 这些注释在这种情况下似乎不起作用。 我还在测试资源中添加了一个 application.properties 并设置了server.port=603 - 也没有运气 您似乎将 MockMvc 与实际的 HTTP 请求混合在一起。您需要选择其中一个并相应地配置和编写您的测试 好吧,可能是这样 - 但只是因为我找不到用 Spring 测试这个的方法 - 这实际上是我的实际问题。我想让服务器运行的情况只是因为我看不到另一种方法来使用 groovys“RESTClient”类执行操作以获得我提供的 REST 接口的一些结果。我真正想知道的是如何使用 Spock 测试框架测试使用 Spring 创建的 REST 控制器 【参考方案1】:

这是你需要的

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class RestHandlerTest extends Specification 

    @Autowired
    TestRestTemplate testRestTemplate

然后你使用 testRestTemplate 而不是客户端。你还需要添加 spock-spring 依赖。

【讨论】:

以上是关于使用带有 Spring Boot 的 Spock 测试框架来测试我的 REST 控制器的主要内容,如果未能解决你的问题,请参考以下文章

在Spring Boot项目中使用Spock测试框架

Spock 和 Spring Boot 集成测试

如何使用 gradle 6+ 和 java 11+ 在 Spring Boot 中配置 spock

使用属性 server.port=0 运行 spock 测试时如何找到 Spring Boot 容器的端口

如何使用 Spring Boot 应用程序和 Spock 测试在运行时更改服务器端口

spring boot单元测试之druid NullPointException问题解决