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

@PactBroker not reading Spring properties with JUnit 5 #1023

Closed
arhohuttunen opened this issue Feb 21, 2020 · 21 comments
Closed

@PactBroker not reading Spring properties with JUnit 5 #1023

arhohuttunen opened this issue Feb 21, 2020 · 21 comments

Comments

@arhohuttunen
Copy link
Contributor

If you provide system properties in Spring application.yml these won't be read by the @PactBroker annotation when running with JUnit 5. This works fine if you provide them as normal system properties.

This issue seems to have been fixed for the JUnit 4 runners in here: #632

I can see that the problem has been fixed by adding a property resolver, and that it is also possible to pass a valueResolver attribute for the @PactBroker annotation. However, since you need a Spring context to be able to create such a resolver for Spring, it cannot be done with the annotation.

I also tried to add a resolver into PactVerificationContext in @BeforeEach but that didn't seem to do the trick.

@vtapadia
Copy link

I am also having the same issue.
The preferred approach to ask questions on stackflow did not help either.
https://stackoverflow.com/questions/60153108/provider-test-integration-with-pact-broker-for-spring-boot-junit5-configuratio

Waiting for the answer

@uglyog
Copy link
Member

uglyog commented Feb 22, 2020

I've created a new module pact-jvm-provider-junit5-spring with a TestTemplateInvocationContextProvider that extends the current JUnit 5 one (PactVerificationSpringProvider). This should allow you to configure all the properties in the Spring context.

@arhohuttunen
Copy link
Contributor Author

That’s awesome! Thank you for such a quick response to the issue. This will make the developer experience smoother when running tests out of the box, with the broker having a default configuration in the properties file.

@vtapadia
Copy link

HI @uglyog , I noticed a small inconsistency in the implementation of the SpringEnvironmentResolver.kt

The propertyDefined() method relies only on environment, while the resolveValue() uses SystemPropertyResolver also as backup.
Also, the Spring environment property resolution considers the system properties also when resolving a value. That has a higher preference. Any specific reason why?

@uglyog
Copy link
Member

uglyog commented Mar 7, 2020

@vtapadia I've removed the SystemPropertyResolver fallback

@uglyog
Copy link
Member

uglyog commented Mar 7, 2020

@vtapadia removing the fallback breaks the pact loader, so I'm reverting the change

uglyog pushed a commit that referenced this issue Mar 7, 2020
@arhohuttunen
Copy link
Contributor Author

Tested this with version 4.0.7 and replaced the PactVerificationInvocationContextProvider with the PactVerificationSpringProvider. Also added the missing properties to application.yml.

However, I'm still getting this:

java.lang.IllegalArgumentException: Invalid pact broker host specified ('${pactbroker.host:}'). Please provide a valid host or specify the system property 'pactbroker.host'.

	at au.com.dius.pact.provider.junit.loader.PactBrokerLoader.getPactBrokerSource(PactBrokerLoader.kt:176)
	at au.com.dius.pact.provider.junit.loader.PactBrokerLoader.description(PactBrokerLoader.kt:62)
	at au.com.dius.pact.provider.junit5.PactVerificationInvocationContextProvider.resolvePactSources(PactJUnit5VerificationProvider.kt:395)
	at au.com.dius.pact.provider.junit5.PactVerificationInvocationContextProvider.provideTestTemplateInvocationContexts(PactJUnit5VerificationProvider.kt:366)

The stack trace suggests that it's still using the PactVerificationInvocationContextProvider and not the Spring specific one.

Am I missing something here?

@arhohuttunen
Copy link
Contributor Author

arhohuttunen commented Mar 14, 2020

I did some debugging with this, and it seems that while the original commit to implement this worked, the change on line 395 of PactJUnit5VerificationProvider.kt on commit 2f1070b has broken the functionality.

It seems that calling it.description() tries to use the resolver, but it has not yet been correctly set.

In PactBrokerLoader.kt:

  override fun description(): String {
    val resolver = setupValueResolver()
    // ...
  }

  private fun setupValueResolver(): ValueResolver {
    var valueResolver: ValueResolver = SystemPropertyResolver()
    // ...
    return valueResolver
  }

uglyog pushed a commit that referenced this issue Mar 21, 2020
fix: @PactBroker not reading Spring properties with JUnit 5 #1023
@uglyog
Copy link
Member

uglyog commented Mar 22, 2020

4.0.8 released with this change

@gustavotpd
Copy link

gustavotpd commented Jul 29, 2020

I'm using this dependency

    <!-- https://mvnrepository.com/artifact/au.com.dius/pact-jvm-provider-junit5 -->
    <dependency>
        <groupId>au.com.dius</groupId>
        <artifactId>pact-jvm-provider-junit5</artifactId>
        <version>4.0.10</version>
    </dependency>

My code:

@PactBroker(scheme = "https", host = "${pactbroker.host}", authentication = @PactBrokerAuth(token = "${pactbroker.auth.token}"))
@Provider("PriceService")
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
@ExtendWith(SpringExtension.class)

public class PriceServicePactTests {

@LocalServerPort
private final int port = 8082;
ObjectMapper objectMapper = new ObjectMapper();
@MockBean
private PriceDAO priceDao;

@BeforeEach
public void setupTest(final PactVerificationContext context) {
	System.setProperty("pact.provider.tag", "dev");
	System.setProperty("pact.provider.version", "1.0.0");
            context.setTarget(new HttpTestTarget("localhost", port, "/"));
}

@BeforeClass
public void enablePublishingPact() {
   	System.setProperty("pact.verifier.publishResults", "true");
}

@TestTemplate
@ExtendWith(PactVerificationInvocationContextProvider.class)
void pactVerificationTestTemplate(final PactVerificationContext context) {
	context.verifyInteraction();
}

and facing the same problem: java.lang.RuntimeException: Could not resolve property "pactbroker.host" in the system properties or environment variables and no default value is supplied

any help would be appreciated.

Thanks in advance.

@uglyog
Copy link
Member

uglyog commented Aug 8, 2020

@gustavotpd pactbroker.host needs to be defined as a JVM system property somewhere. See https://maven.apache.org/surefire/maven-surefire-plugin/examples/system-properties.html on how to do this with the Maven Surfire plugin, which is what runs the tests with Maven.

@vijays2017
Copy link

vijays2017 commented Aug 26, 2020

Hi, I want to disable pact tests in local build . With regards to same I am using @IgnoreNoPactsToVerify (ignoreIoErrors = "${pact.verification.ignoreIoErrors}") and I am setting the property in system arg as mvn -s H:\settings.xml -DargLine="-Dpact.verification.ignoreIoErrors=true however I still get the error -

Could not resolve property "${pact.verification.ignoreIoErrors}" in the system properties or environment variables and no default
value is supplied

I am using pact plugin
<plugin> <groupId>au.com.dius</groupId> <artifactId>pact-jvm-provider-maven_2.12</artifactId> <version>${pact.jvmprovider}</version>

Can you pls help?

@uglyog
Copy link
Member

uglyog commented Sep 5, 2020

@vijays2017 the property needs to be set on the test JVM. See https://maven.apache.org/surefire/maven-surefire-plugin/examples/system-properties.html on how to do this with the Maven Surfire plugin, which is what runs the tests with Maven.

@bijancn
Copy link

bijancn commented Sep 15, 2021

@uglyog @ExtendWith(PactVerificationSpringProvider::class) should really be mentioned in the docs. And also that you can only set usePreemptiveAuthentication via system properties. No matter what loader you hook up.

I ended up with this code to make it work

    @BeforeAll
    fun beforeAll() {
        System.setProperty("pact.pactbroker.httpclient.usePreemptiveAuthentication", "true")
    }

@uglyog
Copy link
Member

uglyog commented Sep 18, 2021

@bijancn Can you expand on what you mean by "should really be mentioned in the docs". It is documented here: https://github.com/pact-foundation/pact-jvm/tree/master/provider/junit5spring#usage

@bijancn
Copy link

bijancn commented Sep 20, 2021

Sure, sorry. In the documentation, it says

@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
@Provider("Animal Profile Service")
@PactBroker
public class ContractVerificationTest {
  
    @TestTemplate
    @ExtendWith(PactVerificationSpringProvider.class)
    void pactVerificationTestTemplate(PactVerificationContext context) {
      context.verifyInteraction();
    }
    
}

So, the PactVerificationSpringProvider is used on the method but not on the class. This does not allow me to load e.g. pactbroker.host from the application.yaml. Instead, I need to do something like this

@ExtendWith(PactVerificationSpringProvider::class) // this allows loading from Spring Context and thus resources/application.yaml and env variables
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
@Provider("Animal Profile Service")
@PactBroker()
public class ContractVerificationTest {

@uglyog
Copy link
Member

uglyog commented Sep 27, 2021

@bijancn I just verified with my test project: https://github.com/uglyog/springboot-junit5-test

That project works loading the broker properties from application.yaml with the annotation on the method. It should make no difference if it is on the method or on the class.

@bijancn
Copy link

bijancn commented Sep 27, 2021

Could it be a Java vs Kotlin thing? It definitely doesn't work for me. But maybe you can also link to your example project? I wasn't aware of that and it would have helped me.

@jmb-missionlane
Copy link

@bijancn I was able to use @ExtendWith(PactVerificationSpringProvider::class) at the method level in my Kotlin project without issue. I did have to use an old version of the dependency though despite my code being Java 11 compliant

testImplementation 'au.com.dius.pact.provider:junit5spring:4.1.10'

Here's my code snippet

@ActiveProfiles("test")
@Provider("Test-Provider")
@PactBroker(
    host = "\${pactbroker.hostname}", scheme = "https",
    authentication = PactBrokerAuth(username = "\${pactbroker.username}", password = "\${pactbroker.password}")
)
@SpringBootTest(classes = [Application::class], webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT, properties = ["server.port=8091"])
class IdentityVerificationPactTest {

    @TestTemplate
    @ExtendWith(PactVerificationSpringProvider::class)
    fun pactVerificationTestTemplate(context: PactVerificationContext, request: HttpRequest) {
        request.addHeader("Content-Type", "application/json")
        // must come after request modification
        context.verifyInteraction()
    }
...

@ViSilver
Copy link

ViSilver commented May 3, 2023

The issue is that the application-test.yaml is not considered unless @ActiveProfiles("test") is specified. I think this is counterintuitive and the main reason for confusion.

@rholshausen
Copy link
Contributor

That is standard Spring behavior. Pact-JVM is not Spring specific.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants