【问题标题】:Connection refused when trying to call Wiremock Stub尝试调用 Wiremock Stub 时连接被拒绝
【发布时间】:2020-12-06 04:45:30
【问题描述】:

我正在尝试将 Cucumber-JVM 与 WireMock 集成,但我一直在尝试

java.net.ConnectException: Connection refused: connect

我尝试了几个教程,包括来自 cucumber.io 的 Official Docs 并且还尝试了以下这些:

Introduction to WireMock from Baeldung

Stuff from StackOverflow

Wiremock Github Issues page

我的 Gradle 依赖项:

implementation 'org.springframework.boot:spring-boot-starter-web'   
testImplementation 'io.cucumber:cucumber-java:6.2.2'
testImplementation 'io.cucumber:cucumber-junit:6.2.2'
testImplementation 'io.rest-assured:spring-web-test-client:4.3.1'
testCompile "com.github.tomakehurst:wiremock-jre8:2.27.0"
compile group: 'io.cucumber', name: 'cucumber-spring', version: '6.4.0'

基本思想是模拟服务器响应,因此将来我将能够在多个微服务之间创建一些集成测试。 这个想法来自我正在阅读的一本书 The Cucumber for Java Book 如果有更好的方法来测试微服务,我愿意接受新的想法。

我有一个带有步骤定义的测试类,它从属性文件中获取端口信息。如下:

@SpringBootTest
@CucumberContextConfiguration
public class ConnectionWithCucumber {

    @Value("${another.server.port}")
    private int PORT;

    @Rule
    private WireMockRule wireMockRule = new WireMockRule(PORT);

    private String messageResult;

    @Given("I want to read a message")
    public void iWantToRead() {
        createMessageStub();
    }

    @When("I send the request")
    public void iSendTheRequest() {
        messageResult = given().get("localhost:8082/message").getBody().asString();
    }

    @Then("I should be able to read the word {string}")
    public void iShouldBeAbleToReadTheWord(String arg0) {
        assertEquals(arg0, messageResult);
    }

    private void createMessageStub() {
        wireMockRule.stubFor(get(urlEqualTo("/message"))
                .withHeader("Accept", equalTo("application/json"))
                .willReturn(aResponse()
                        .withStatus(200)
                        .withHeader("Content-Type", "application/json")
                        .withBody("message")));
    }
}

我还创建了一个带有可运行示例的repository

如果您没有找到 README 文件,在查看 repo 时,您可以使用以下命令运行项目:

./gradlew cucumber

或者如果您使用的是 Windows:

gradle cucumber

在我让它工作之后,我重构了代码并将示例留在了我上面链接的存储库中,如果你有同样的问题,请检查它。

【问题讨论】:

    标签: java spring gradle cucumber-jvm wiremock


    【解决方案1】:

    java.net.ConnectException: Connection denied: connect 这意味着没有服务在监听端口请尝试打印服务正在运行的端口并检查。我可以看到你已经检查了wiremock是否正在运行,请检查端口

    您可以像这样添加测试属性。这将覆盖默认的 application.properties

        @TestPropertySource(properties = {
            "application.location=http://localhost:8082/app/api/v1/"
    
    })
    

    请将本行中的url改为

     Header header = new Header("Accept","application/json")
     messageResult = given().header(header).port(8082).get("/message").getBody().asString();
    

    而不是

      messageResult = given().get("localhost:8082/message").getBody().asString();
    

    它对我有用

    @SpringBootTest
    @CucumberContextConfiguration
    public class ConnectionWithCucumber {
    
        @Value("${another.server.port}")
        private int PORT;
    
    
        @Rule
        private WireMockRule wireMockRule = new WireMockRule(8082);
    
        private WireMockServer wireMockServer = new WireMockServer(8083);
    
    
        private String messageResult;
    
        @Value("${test.word}")
        private String word;
    
        @Value("${test.number}")
        private String number;
    
        @Test
        public void testWord(){
            wireMockServer.start();
            wireMockRule.start();
            wireMockRule.isRunning();
            wireMockServer.isRunning();
            System.out.println(wireMockRule.port());
            assertThat(word).isEqualTo("word");
            assertThat(number).isEqualTo("number");
        }
    
    
        @Given("I want to read a message")
        public void iWantToRead() {
            wireMockServer.start();
            wireMockRule.start();
            wireMockRule.isRunning();
            wireMockServer.isRunning();
            System.out.println("wireMockRule port " + wireMockRule.port());
            System.out.println("wireMockServer port " + wireMockServer.port());
    
            // Start the stub
            createMessageStubServer();
            createMessageStub();
    
            wireMockServer.getStubMappings();
            wireMockRule.getStubMappings();
        }
    
        @When("I send the request")
        public void iSendTheRequest() {
            System.out.println("iSendTheRequest" + wireMockRule.isRunning());
            System.out.println("iSendTheRequest" + wireMockServer.isRunning());
            Header header = new Header("Content-Type","application/json");
            messageResult = given().port(8082).and().header("Accept","application/json").and()
                    .get("/message").getBody().asString();
    
            System.out.println(messageResult);
    
        }
    
        @Then("I should be able to read the word {string}")
        public void iShouldBeAbleToReadTheWord(String arg0) {
            System.out.println(messageResult);
            System.out.println("arg0"+arg0);
            assertEquals(arg0, messageResult);
        }
    
        private void createMessageStub() {
            wireMockRule.stubFor(get(urlEqualTo("/message"))
                    .withHeader("Accept", equalTo("application/json"))
                    .willReturn(aResponse()
                            .withStatus(200)
                            .withHeader("Content-Type", "application/json")
                            .withBody("Response")));
        }
    
        private void createMessageStubServer() {
            wireMockServer.stubFor(get(urlEqualTo("/message"))
                    .withHeader("Accept", equalTo("application/json"))
                    .willReturn(aResponse()
                            .withStatus(200)
                            .withHeader("Content-Type", "application/json")
                            .withBody("{\"message\":\"1\"}")));
        }
    }
    

    这是您使用线模拟规则和线模拟服务器的工作代码,我们不需要根据文档同时使用两者,您可以单独使用线模拟规则,这足以在每次测试之前启动和停止服务器

    请参考 http://wiremock.org/docs/getting-started/

    不要使用随机端口,因为这个测试用例可能会在其他环境中失败,使用固定端口,就像你对代码所做的那样

    您可以使用wiremock 规则或使用@AutoConfigureWireMock 的spring 方式,它会自动注入依赖项,因此spring 将启动和停止模拟服务器而不是junit。

    请参考https://cloud.spring.io/spring-cloud-contract/reference/html/project-features.html#features-wiremock 了解弹簧线模拟文档

    这里还有一点需要注意,@Rule 在 spring 之前执行,因此它没有获取端口值,因为 @Rule 属于 junit,您只需在 @Rule 注释中对端口进行硬编码,或者您可以使用 @AutoConfigureWireMock,即我硬编码的原因

    【讨论】:

    • Ramaman,虽然文中没有(还)提到它,但我已经在PORT 变量上运行了一个调试,以检查是否接收到一个值,它是..关于覆盖属性,这会给我带来另一个问题,因为将来我将使用多个配置文件。不过还是谢谢。
    • 我已经按照你说的替换了 URL,但仍然收到连接被拒绝。
    【解决方案2】:

    WireMockRule 依赖于来自 JUnit 4 的 @Rule 注释。在 Cucumber 中使用时没有任何作用。而是考虑使用spring-boot-starter-web 中的@AutoConfigureWireMock 来设置wiremock。

    ├── pom.xml
    └── src
        ├── main
        │   └── java
        │       └── com
        │           └── example
        │               └── Application.java
        └── test
            ├── java
            │   └── com
            │       └── example
            │           └── CucumberTest.java
            └── resources
                ├── application.yml
                ├── com
                │   └── example
                │       └── hello.feature
                └── junit-platform.properties
    
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns="http://maven.apache.org/POM/4.0.0"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.3.1.RELEASE</version>
        </parent>
    
        <groupId>com.example</groupId>
        <artifactId>com.example</artifactId>
        <version>1.0.0-SNAPSHOT</version>
    
        <properties>
            <java.version>11</java.version>
            <cucumber.version>6.5.0</cucumber.version>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <spring-cloud.version>Hoxton.SR7</spring-cloud.version>
        </properties>
    
    
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-dependencies</artifactId>
                    <version>${spring-cloud.version}</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
            </dependencies>
        </dependencyManagement>
    
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-contract-stub-runner</artifactId>
                <scope>test</scope>
            </dependency>
    
            <dependency>
                <groupId>io.cucumber</groupId>
                <artifactId>cucumber-java</artifactId>
                <version>${cucumber.version}</version>
                <scope>test</scope>
            </dependency>
    
            <dependency>
                <groupId>io.cucumber</groupId>
                <artifactId>cucumber-spring</artifactId>
                <version>${cucumber.version}</version>
                <scope>test</scope>
            </dependency>
    
            <dependency>
                <groupId>io.cucumber</groupId>
                <artifactId>cucumber-junit-platform-engine</artifactId>
                <version>${cucumber.version}</version>
                <scope>test</scope>
            </dependency>
    
        </dependencies>
    </project>
    
    package com.example;
    
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.boot.web.client.RestTemplateBuilder;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.client.RestTemplate;
    
    @SpringBootApplication
    public class Application {
    
        @Configuration
        @ConfigurationProperties("com.example")
        public static class HelloConfiguration {
    
            String helloService;
    
            public String getHelloService() {
                return helloService;
            }
    
            public void setHelloService(String helloService) {
                this.helloService = helloService;
            }
    
        }
    
        @RestController
        public static class HelloController {
    
            private final RestTemplate helloService;
    
            public HelloController(
                    RestTemplateBuilder restTemplateBuilder,
                    HelloConfiguration configuration) {
                this.helloService = restTemplateBuilder
                        .rootUri(configuration.getHelloService())
                        .build();
            }
    
            @RequestMapping("/local")
            public String local() {
                return "Greetings from Local!";
            }
    
            @RequestMapping("/remote")
            public String remote() {
                return helloService.getForObject("/", String.class);
            }
    
        }
    
    }
    
    package com.example;
    
    import com.github.tomakehurst.wiremock.client.WireMock;
    import io.cucumber.java.en.Given;
    import io.cucumber.junit.platform.engine.Cucumber;
    import io.cucumber.spring.CucumberContextConfiguration;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.cloud.contract.wiremock.AutoConfigureWireMock;
    import org.springframework.test.web.servlet.MockMvc;
    
    import static com.github.tomakehurst.wiremock.client.WireMock.okJson;
    import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
    import static org.hamcrest.CoreMatchers.equalTo;
    import static org.springframework.http.MediaType.APPLICATION_JSON;
    import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
    import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
    
    @Cucumber
    @CucumberContextConfiguration
    @SpringBootTest
    @AutoConfigureMockMvc
    @AutoConfigureWireMock(port = 0)
    public class CucumberTest {
    
        @Autowired
        private MockMvc mvc;
    
        @Given("the application says hello")
        public void getLocalHello() throws Exception {
            mvc.perform(get("/local").accept(APPLICATION_JSON))
                    .andExpect(status().isOk())
                    .andExpect(content().string(equalTo("Greetings from Local!")));
        }
    
        @Given("the stub says hello")
        public void getRemoteHello() throws Exception {
            stubFor(WireMock.get("/").willReturn(okJson("Greetings from Stub!")));
    
            mvc.perform(get("/remote").accept(APPLICATION_JSON))
                    .andExpect(status().isOk())
                    .andExpect(content().string(equalTo("Greetings from Stub!")));
        }
    
    }
    
    Feature: Hello world
    
      Scenario: Calling a rest end point
        * the application says hello
        * the stub says hello
    
    com:
      example:
        hello-service: http://localhost:${wiremock.server.port}
    

    【讨论】:

    • 好吧,我从你的代码中复制了一份并粘贴到我的项目上,而不是得到一个拒绝连接我得到一个 404。我什至将“存根打招呼”更改为“/remote " 而不是 "/",但没有成功。
    • 我又来了。我回来逐行阅读您的答案,问题出在@AutoConfigureWireMock上。我不得不做一些重构,但让它工作。谢谢。
    猜你喜欢
    • 2020-01-29
    • 2019-12-28
    • 2021-11-16
    • 2015-03-09
    • 2019-10-20
    • 2021-12-23
    • 2018-12-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多