Search code examples
spring-bootmockitopact

Unable to mock service layer using mockito in pact provider test


I am trying to implement pact testing for my APIs. I am able to define the contract on the consuming side and upload it to the Pact broker. On my provider API, I have fetched the contract from the broker but when trying to verify the contract, my test is failing because the service layer is being executed instead of being stubbed - which in turn results to ResourceNotFoundException: Client client1 with app myapp not found - A custom exception thrown if no client found in database.

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ExtendWith(SpringExtension.class)
@Provider("lockedOut_identityService")
@PactBroker(url = "http://localhost:9292")
@ExtendWith(MockitoExtension.class)
public class AuthServiceProviderTest {

    @Mock
    private ClientService clientService;

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

    @BeforeEach
    void before(PactVerificationContext context) {
        context.setTarget(new HttpTestTarget("localhost", port, "/"));
    }

    @TestTemplate
    @ExtendWith(PactVerificationInvocationContextProvider.class)
    void pactVerificationTestTemplate(PactVerificationContext context) {
        if (context != null) {
            context.verifyInteraction();
        }
    }

    @State("Get client by appName & clientId")
    public void getClient() {
        when(clientService.getClient(eq(null), any(String.class), any(String.class)))
                .thenReturn(ClientResponse.builder()
                        .id(1L)
                        .clientId("client1")
                        .clientSecret("secret")
                        .authMethod(null)
                        .authGrantType(null)
                        .redirectUri("redirectUri")
                        .createdAt(LocalDateTime.now())
                        .build());
    }
}

It seems as though the when(clientService.getClient(eq(null), any(String.class), any(String.class))) is not actually stubbing the service layer.

Controller:

@GetMapping("/get-client")
public ResponseEntity<ClientResponse> getClient(
        @RequestHeader(value = "x-correlation-id", required = true) String correlationId,
        @RequestParam(value = "id", required = false) Long id,
        @RequestParam(value = "appName", required = false) String appName,
        @RequestParam(value = "clientId", required = false) String clientId
) {
    if ((id == null && appName != null && clientId == null)
            || (id == null && appName == null)) {
        throw new InvalidRequestException(ErrorConstant.INVALID_REQUEST.getValue());
    }

    return new ResponseEntity<>(clientService.getClient(id, appName, clientId), HttpStatus.OK);
}

Controller endpoint being invoked with values: id: null, appname: "myapp", clientId: "client1"

What have I missed? Seems like from Mockito end, things look fine. Have I missed any additional Pact config to use the Mockito stubbing?

Using dependencies:

implementation 'au.com.dius.pact.provider:junit5:4.6.3'

Solution

  • If you are using @SpringBootTest you need to annotate mocked objects with @MockBean to get them injected into Spring context. This means that beans created by Spring will use your @MockBean as their dependencies.

    See Difference between @Mock, @MockBean and Mockito.mock()