Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

@InjectWireMock not working in conjunction with Cucumber #73

Open
toellrich opened this issue Dec 12, 2024 · 12 comments
Open

@InjectWireMock not working in conjunction with Cucumber #73

toellrich opened this issue Dec 12, 2024 · 12 comments
Labels
bug Something isn't working

Comments

@toellrich
Copy link

Proposal

I'm testing my spring boot application with Cucumber. Unfortunately, @InjectWireMock doesn't seem to be working in that case.

Reproduction steps

I have a feature file in src/test/resources/features

Feature: Foo
  Scenario: Foo
    When foo

An application class in src/man/java:

package test;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

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

A test class in src/test/java

package test;

import static io.cucumber.junit.platform.engine.Constants.GLUE_PROPERTY_NAME;

import org.junit.platform.suite.api.ConfigurationParameter;
import org.junit.platform.suite.api.IncludeEngines;
import org.junit.platform.suite.api.SelectClasspathResource;
import org.junit.platform.suite.api.Suite;

@Suite
@IncludeEngines("cucumber")
@SelectClasspathResource("features")
@ConfigurationParameter(key = GLUE_PROPERTY_NAME, value = "test")
public class ApplicationTest {
}

And a steps class also in src/test/java:

package test;

import static org.assertj.core.api.Assertions.assertThat;

import com.github.tomakehurst.wiremock.WireMockServer;
import io.cucumber.java.en.When;
import io.cucumber.spring.CucumberContextConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.wiremock.spring.EnableWireMock;
import org.wiremock.spring.InjectWireMock;

@CucumberContextConfiguration
@EnableWireMock
@SpringBootTest
public class ApplicationSteps {

  @InjectWireMock
  private WireMockServer wireMockServer;

  @When("foo")
  public void foo() {
    assertThat(wireMockServer).isNotNull();
  }
}

The test fails since wireMockServer is null. I'm using Java 21 and the latest versions of Spring Boot (3.4.0), Cucumber (7.20.1) and WireMock Spring Boot (3.4.0).

References

No response

@toellrich toellrich added the bug Something isn't working label Dec 12, 2024
@toellrich
Copy link
Author

I'm attaching the project files
test.zip

@tomasbjerre
Copy link
Collaborator

tomasbjerre commented Dec 12, 2024

I think the problem here is that the Junit5-extension is not triggered. None of the functionality it provides is available. Not sure if that can, or should, be fixed here.

But perhaps you only need the Spring features. Something like:

public class ApplicationSteps {

	@Value("${wiremock.server.port}")
	private int wiremockPort;

	@When("foo")
	public void foo() {
		WireMock.configureFor(wiremockPort);
		WireMock.reset(); // Not sure where, or if, you want/need this.
                
                // Configure with code maby?
		WireMock.stubFor(get("/ping").willReturn(aResponse().withStatus(200)));
	}
}

And if you don't want to configure with code, it will still look for configuration as stated in the log:

2024-12-12T10:52:46.043+01:00  INFO 7895 --- [           main] c.w.s.i.WireMockServerCreator wiremock   : Looking for mocks in directory wiremock/wiremock... 
2024-12-12T10:52:46.044+01:00  INFO 7895 --- [           main] c.w.s.i.WireMockServerCreator wiremock   : Looking for mocks in directory stubs/wiremock... 
2024-12-12T10:52:46.044+01:00  INFO 7895 --- [           main] c.w.s.i.WireMockServerCreator wiremock   : Looking for mocks in directory mappings/wiremock... 
2024-12-12T10:52:46.045+01:00  INFO 7895 --- [           main] c.w.s.i.WireMockServerCreator wiremock   : Looking for mocks in directory wiremock... 
2024-12-12T10:52:46.045+01:00  INFO 7895 --- [           main] c.w.s.i.WireMockServerCreator wiremock   : Looking for mocks in directory stubs... 
2024-12-12T10:52:46.045+01:00  INFO 7895 --- [           main] c.w.s.i.WireMockServerCreator wiremock   : Looking for mocks in directory mappings... 
2024-12-12T10:52:46.046+01:00  INFO 7895 --- [           main] c.w.s.i.WireMockServerCreator wiremock   : No mocks found under directory
2024-12-12T10:52:46.047+01:00  INFO 7895 --- [           main] c.w.s.i.WireMockServerCreator wiremock   : No mocks found under classpath

@toellrich
Copy link
Author

Thanks for the quick feedback. I tried your suggestion

@CucumberContextConfiguration
@EnableWireMock
@SpringBootTest
public class ApplicationSteps {

  @Value("${wiremock.server.port}")
  private int wiremockPort;

  @When("foo")
  public void foo() {
    WireMock.reset();
    WireMock.configureFor(wiremockPort);
    // assertThat(wireMockServer).isNotNull();
  }
}

but I'm getting the following exception:

org.apache.hc.client5.http.HttpHostConnectException: Connect to http://localhost:8080 [localhost/127.0.0.1, localhost/0:0:0:0:0:0:0:1] failed: Connection refused: no further information
	at java.base/sun.nio.ch.Net.pollConnect(Native Method)
	at java.base/sun.nio.ch.Net.pollConnectNow(Net.java:682)
	at java.base/sun.nio.ch.NioSocketImpl.timedFinishConnect(NioSocketImpl.java:542)
	at java.base/sun.nio.ch.NioSocketImpl.connect(NioSocketImpl.java:592)
	at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:327)
	at java.base/java.net.Socket.connect(Socket.java:751)
	at org.apache.hc.client5.http.impl.io.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator.java:216)
	at org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:490)
	at org.apache.hc.client5.http.impl.classic.InternalExecRuntime.connectEndpoint(InternalExecRuntime.java:164)
	at org.apache.hc.client5.http.impl.classic.InternalExecRuntime.connectEndpoint(InternalExecRuntime.java:174)
	at org.apache.hc.client5.http.impl.classic.ConnectExec.execute(ConnectExec.java:144)
	at org.apache.hc.client5.http.impl.classic.ExecChainElement.execute(ExecChainElement.java:51)
	at org.apache.hc.client5.http.impl.classic.ProtocolExec.execute(ProtocolExec.java:192)
	at org.apache.hc.client5.http.impl.classic.ExecChainElement.execute(ExecChainElement.java:51)
	at org.apache.hc.client5.http.impl.classic.InternalHttpClient.doExecute(InternalHttpClient.java:174)
	at org.apache.hc.client5.http.impl.classic.CloseableHttpClient.execute(CloseableHttpClient.java:123)
	at com.github.tomakehurst.wiremock.client.HttpAdminClient.safelyExecuteRequest(HttpAdminClient.java:546)
	at com.github.tomakehurst.wiremock.client.HttpAdminClient.postJsonAssertOkAndReturnBody(HttpAdminClient.java:452)
	at com.github.tomakehurst.wiremock.client.HttpAdminClient.resetAll(HttpAdminClient.java:200)
	at com.github.tomakehurst.wiremock.client.WireMock.resetMappings(WireMock.java:409)
	at com.github.tomakehurst.wiremock.client.WireMock.reset(WireMock.java:413)
	at test.ApplicationSteps.foo(ApplicationSteps.java:20)
	at ✽.foo(classpath:features/hello.feature:3)

@tomasbjerre
Copy link
Collaborator

Put reset after configureFor. I updated my comment with that.

@toellrich
Copy link
Author

toellrich commented Dec 12, 2024

This does seem to work. Thanks a lot! The issue can be closed. You might want to consider adding this to the documentation.

@toellrich
Copy link
Author

toellrich commented Dec 14, 2024

I have a good workaround now, but I was wondering why don't you add the WireMockServer bean to the Spring context (just like you add those two properties), so that it can simply be injected via @Autowired.

@tomasbjerre
Copy link
Collaborator

In an earlier revision of the readme:
https://github.com/wiremock/wiremock-spring-boot/tree/v2.1.3

It says "does not pollute spring application context with extra beans". But I dont know exactly what the problem was, it seems intended.

@tomakehurst
Copy link
Member

@maciejwalkowiak what was your original motivation for keeping WireMock out of the Spring context?

Have you found it added significant overhead to do it that way?

@maciejwalkowiak
Copy link
Collaborator

The original motivation was to avoid amending Spring context and risk slowing down tests because of that (basically avoid what @MockBean does to test context and issues caused by that). Because it was quite easy to achieve everything I needed without adding WireMock as a bean, I made a lot of sense to keep it that way.

Perhaps there are cases like usage with Cucumber where it would make more sense to add it as a bean.

@genuss
Copy link

genuss commented Jan 15, 2025

I'd like an example of such case. When we write integration tests, we create separate beans in test context which encapsulate some logic. One example may be an HTTP fixture, which manipulates wiremock. So the simplified setup looks like this:

class SomeTest extends AbstractIntegrationTest {
  @Test
  void test() {
    httpFixture.setUp();
    // proceed with test
  }
}

@SpringBootTest
class AbstractIntegrationTest {
  @Autowired protected HttpServerMockFixture httpFixture;
}

@Component
class HttpServerMockFixture {
  private final WireMockServer wireMockServer;

  HttpServerMockFixture(@InjectWireMock WireMockServer wireMockServer) {
    this.wireMockServer = wireMockServer;
  }

  void setUp() {
    // wireMockServer manipulation
  }
}

This example fails as WireMockServer is not a bean and `@InjectWireMock` works only in test classes. If `WireMockServer` is a singleton, I don't actually see downsides, as spring context isn't invalidated for every test.

@maciejwalkowiak
Copy link
Collaborator

Sure, perhaps the benefits overweight the downsides. There are some edge cases to handle - when multiple WireMock servers are registered as beans, you need a qualifier to inject them etc.

@tomakehurst if it is beneficial for the project and community to register servers as beans I don't see a reason to not do it. The only thing that I believe should stay is registering multiple servers in single project as this is something that distinguishes this project from Spring Cloud WireMock.

@tomakehurst
Copy link
Member

It sounds like we could give the Cucumber folks a helping hand if we did this, and maybe if it was only activated if you explicitly opt into it then we avoid the overhead in cases where we don't need a bean?

Maybe we could just say that you have to give it a qualifier if you want it in the Spring context, and if you omit that parameter it won't be added?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

5 participants