英文:
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
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
│   └── 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}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论