April 12, 2019
When using the Spring Pact provider library provided by DiUS, verifying pacts that your Spring applications provide is super easy. The tests themselves barely have any code in them - in fact they look like they don’t contain any tests!
However the library uses the JUnit4 @RunWith
annotation which is no longer supported with Junit5, so when I needed to upgrade JUnit I had to find a way to port our pact tests across.
It turns out it’s very easy and, despite requiring more lines of code, it’s more explicit and more extensible.
Imagine a provider UniqueIdProvider
.
The tests started out as:
@Provider("UniqueIdProvider")
@PactBroker(tags = {"master", "development", "production"})
@VerificationReports
@RunWith(SpringRestPactRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class ContractTest {
@TestTarget
public final Target target = new SpringBootHttpTarget();
@MockBean
private UniqueIdService uniqueIdService;
@State("Unique ID service returns 1234")
public void uniqueId() {
given(uniqueIdService.getUniqueId()).willReturn("1234");
}
}
And after adding testCompile('au.com.dius:pact-jvm-provider-junit5_2.12:3.6.3')
to gradle (along with the required JUnit dependencies) the test became:
@Provider("UniqueIdProvider")
@PactBroker(tags = {"master", "development", "production"}, host = "pact-broker.tools", port = "443")
@VerificationReports
@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class ContractTest {
@LocalServerPort
int localServerPort;
@MockBean
private UniqueIdService uniqueIdService;
@BeforeEach
void setUp(PactVerificationContext context) {
context.setTarget(new HttpTestTarget("localhost", localServerPort, "/"));
}
@TestTemplate
@ExtendWith(PactVerificationInvocationContextProvider.class)
void testTemplate(PactVerificationContext context) {
context.verifyInteraction();
}
@State("Unique ID service returns 1234")
public void uniqueId() {
given(uniqueIdService.getUniqueId()).willReturn("1234");
}
}
The changes here are that @RunWith(SpringRunner.class)
is replaced with @ExtendWith(SpringExtension.class)
to run the test in a Spring context. This will set up the controllers and services which the pact verification will run against.
Previously SpringBootHttpTarget
set up the target from the random port automatically but now we need to set the PactVerificationContext
’s target for each test in a @BeforeEach
annotated method.
One of the things I like is that using JUnit5’s TestTemplate
means what is being tested is now explicit. Before, it was implicit and hidden with the JUnit4 method - notice how there was no @Test
method in the JUnit4 test class.
The other thing I like is that this format is extensible - other extensions can be added to the class and the test is just running the Spring context using the standard extension. This means less things to learn and all standard Spring configuration and test practices will simply work.
Lastly the broker host and port need to be passed explicitly to the @PactBroker
annotation since it can’t be inferred from the application properties anymore (this is something the SpringRestPactRunner
used to do).
The migration was much simpler than I expected: just a combination of standard Spring test annotations and JUnit5 Pact additions.
Written by Phil Hardwick