连接被拒绝,尝试调用Wiremock Stub时。

huangapple go评论73阅读模式
英文:

Connection refused when trying to call Wiremock Stub

问题

我正在尝试将Cucumber-JVM与WireMock集成,但是一直遇到以下错误:

    java.net.ConnectException: 连接被拒绝: connect

我尝试了几个教程,包括来自cucumber.io的[官方文档][1],还尝试了以下资料:

[Baeldung上的WireMock介绍][2]

[StackOverflow上的一些讨论][3]

[Wiremock的Github问题页面][4]

我的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][5]。
如果有更好的方法来测试微服务,我也可以接受新的想法。

我有一个测试类,其中包含我的步骤定义,该类从属性文件中获取端口信息,如下所示:

    @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")));
        }
    }

我还创建了一个包含可运行示例的[存储库][6]。

如果在查看存储库时找不到README文件,您可以使用以下命令运行项目:

    ./gradlew cucumber

如果您使用的是Windows系统:

    gradle cucumber

在我解决问题后,我重构了代码,并将示例留在了上面链接的存储库中,如果您遇到相同的问题,请查看一下。

  [1]: http://wiremock.org/docs/junit-rule/
  [2]: https://www.baeldung.com/introduction-to-wiremock
  [3]: https://stackoverflow.com/questions/57626072/connection-refused-when-using-wiremock
  [4]: https://github.com/tomakehurst/wiremock/issues/20
  [5]: https://www.amazon.com.br/dp/B00V20IEXM/ref=dp-kindle-redirect?_encoding=UTF8&btkr=1
  [6]: https://github.com/JohnneSouza/cucumber-wiremock-integration
英文:

I'm trying to integrate Cucumber-JVM with WireMock and I keep getting

java.net.ConnectException: Connection refused: connect

I've tried several tutorials, including the Official Docs from cucumber.io
And also tried these below:

Introduction to WireMock from Baeldung

Stuff from StackOverflow

Wiremock Github Issues page

My Gradle dependencies:

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 basic idea is to Mock a Server Response, so in the future I'll be able to create some integration tests between several microservices.
The idea came from a book while I'm reading
The Cucumber for Java Book
If there are better ways to test microservices I'm open to new ideas.

I've a Test Class with my Step Definitions that getting the port info form a propertie file. Like below:

@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")));
    }
}

And I've also created a repository with a runnable example.

If you don't find a README file, while looking the repo you can run the project by using:

./gradlew cucumber

or if you are on Windows:

gradle cucumber

After I got it working, I refactored the code and left the example on the repository I've linked above, if you are have the same problem check it out.

答案1

得分: 3

java.net.ConnectException: 连接被拒绝连接这意味着在端口上没有服务在监听请尝试打印服务正在运行的端口并进行检查我可以看到您已经检查了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\"}")));
    }
}

这是您正在使用的工作代码您无需同时使用WireMock Rule和WireMock Server根据文档您可以仅使用WireMock Rule这已经足够了它会在每个测试之前启动和停止服务器

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

不要使用随机端口因为这会导致测试用例在其他环境中可能失败使用像您代码中所做的固定端口

您可以使用WireMock Rule或使用Spring的方式使用@AutoConfigureWireMock它会自动注入依赖因此Spring会启动和停止模拟服务器而不是JUnit

请参考https://cloud.spring.io/spring-cloud-contract/reference/html/project-features.html#features-wiremock 以获取Spring WireMock文档。

还有一件事要注意@Rule在Spring之前执行因此它无法获取端口值因为@Rule属于JUnit您只能在@Rule注释中硬编码端口或者您可以使用@AutoConfigureWireMock这就是我硬编码端口的原因
英文:

java.net.ConnectException: Connection refused: connect this means there is no service listening on the port please try to print the port in which the service is running and check. I can see u have already checked whether wiremock is running or not please do check the port also

You can add test property like this. that will override the default application.properties

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

please change the url in the line to

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

instead of

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

it is working for me

@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\"}")));
}
}

This is the working code you are using wire mock rule and wire mock server we need not use both as per documenation u can use wiremock rule alone that is enough it will start and stop the server before every test

please refer
http://wiremock.org/docs/getting-started/

Don't use random port because of this test cases might fail in other environments use fixed port as you did with your code

You can either use wiremock rule or spring way of using @AutoConfigureWireMock it will auto inject dependecies so spring will start and stop the mock server instead of junit.

please refer https://cloud.spring.io/spring-cloud-contract/reference/html/project-features.html#features-wiremock for spring wire mock docs

one more thing to note here @Rule is executed before spring so it is not getting the port value since @Rule belongs to junit you have to only hard code the port in @Rule annotation or you can use @AutoConfigureWireMock that is the reason i have hardcoded it

答案2

得分: 2

The WireMockRule depends on the @Rule annotation which comes from JUnit 4. It doesn't have any effect when used in Cucumber. Instead consider using @AutoConfigureWireMock from spring-boot-starter-web to setup 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>
        <!-- Other dependencies -->
    </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 {
    // Application class content
}
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;

// CucumberTest class content
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}
英文:

The WireMockRule depends on the @Rule annotation which comes from JUnit 4. It doesn't have any effect when used in Cucumber. Instead consider using @AutoConfigureWireMock from spring-boot-starter-web to setup wiremock.

├── pom.xml
└── src
├── main
│&#160;&#160; └── java
│&#160;&#160;     └── com
│&#160;&#160;         └── example
│&#160;&#160;             └── Application.java
└── test
├── java
│&#160;&#160; └── com
│&#160;&#160;     └── example
│&#160;&#160;         └── CucumberTest.java
└── resources
├── application.yml
├── com
│&#160;&#160; └── example
│&#160;&#160;     └── hello.feature
└── junit-platform.properties
&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;project xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
xmlns=&quot;http://maven.apache.org/POM/4.0.0&quot;
xsi:schemaLocation=&quot;http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd&quot;&gt;
&lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;
&lt;parent&gt;
&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
&lt;artifactId&gt;spring-boot-starter-parent&lt;/artifactId&gt;
&lt;version&gt;2.3.1.RELEASE&lt;/version&gt;
&lt;/parent&gt;
&lt;groupId&gt;com.example&lt;/groupId&gt;
&lt;artifactId&gt;com.example&lt;/artifactId&gt;
&lt;version&gt;1.0.0-SNAPSHOT&lt;/version&gt;
&lt;properties&gt;
&lt;java.version&gt;11&lt;/java.version&gt;
&lt;cucumber.version&gt;6.5.0&lt;/cucumber.version&gt;
&lt;project.build.sourceEncoding&gt;UTF-8&lt;/project.build.sourceEncoding&gt;
&lt;spring-cloud.version&gt;Hoxton.SR7&lt;/spring-cloud.version&gt;
&lt;/properties&gt;
&lt;dependencyManagement&gt;
&lt;dependencies&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-dependencies&lt;/artifactId&gt;
&lt;version&gt;${spring-cloud.version}&lt;/version&gt;
&lt;type&gt;pom&lt;/type&gt;
&lt;scope&gt;import&lt;/scope&gt;
&lt;/dependency&gt;
&lt;/dependencies&gt;
&lt;/dependencyManagement&gt;
&lt;dependencies&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
&lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
&lt;artifactId&gt;spring-boot-starter-test&lt;/artifactId&gt;
&lt;scope&gt;test&lt;/scope&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-starter-contract-stub-runner&lt;/artifactId&gt;
&lt;scope&gt;test&lt;/scope&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;io.cucumber&lt;/groupId&gt;
&lt;artifactId&gt;cucumber-java&lt;/artifactId&gt;
&lt;version&gt;${cucumber.version}&lt;/version&gt;
&lt;scope&gt;test&lt;/scope&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;io.cucumber&lt;/groupId&gt;
&lt;artifactId&gt;cucumber-spring&lt;/artifactId&gt;
&lt;version&gt;${cucumber.version}&lt;/version&gt;
&lt;scope&gt;test&lt;/scope&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;io.cucumber&lt;/groupId&gt;
&lt;artifactId&gt;cucumber-junit-platform-engine&lt;/artifactId&gt;
&lt;version&gt;${cucumber.version}&lt;/version&gt;
&lt;scope&gt;test&lt;/scope&gt;
&lt;/dependency&gt;
&lt;/dependencies&gt;
&lt;/project&gt;
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(&quot;com.example&quot;)
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(&quot;/local&quot;)
public String local() {
return &quot;Greetings from Local!&quot;;
}
@RequestMapping(&quot;/remote&quot;)
public String remote() {
return helloService.getForObject(&quot;/&quot;, 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(&quot;the application says hello&quot;)
public void getLocalHello() throws Exception {
mvc.perform(get(&quot;/local&quot;).accept(APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().string(equalTo(&quot;Greetings from Local!&quot;)));
}
@Given(&quot;the stub says hello&quot;)
public void getRemoteHello() throws Exception {
stubFor(WireMock.get(&quot;/&quot;).willReturn(okJson(&quot;Greetings from Stub!&quot;)));
mvc.perform(get(&quot;/remote&quot;).accept(APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().string(equalTo(&quot;Greetings from Stub!&quot;)));
}
}
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}

huangapple
  • 本文由 发表于 2020年8月17日 11:35:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/63444220.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定