0

So Im writing unit test a class that does bankId authentication and the unit test looks like this.

package se.kt.client;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.client.RestTemplate;
import org.springframework.http.HttpEntity;
import se.kt.common.vo.PublicApplicationForm;
import se.kt.models.BankIdAuthRequest;
import se.kt.models.BankIdAuthResponse;
import se.kt.models.BankIdCollectResponse;
import static org.mockito.ArgumentMatchers.any;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;


@RunWith(MockitoJUnitRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class BankIdClientTest {

    private final RestTemplate restTemplate = Mockito.mock(RestTemplate.class);

    @InjectMocks
    private BankIdClient bankIdClient;

    @BeforeEach
    public void setUp() {
    }



    @Test
    public void testBankIdAuthentication_success() throws InterruptedException {
        PublicApplicationForm form = new PublicApplicationForm();
        form.setSsn("123456-7890");
        form.setIp_address("123.123.123.123");

        BankIdAuthRequest authRequest = bankIdClient.authRequestFromApplicationForm(form, "123");

        BankIdAuthResponse authResponse = new BankIdAuthResponse();
        authResponse.setOrderRef("123456");

        BankIdCollectResponse collectResponse = new BankIdCollectResponse();
        collectResponse.setStatus("completed");

        Mockito.when(restTemplate.postForEntity(anyString(), any(HttpEntity.class), any()))
                .thenReturn(ResponseEntity.ok(authResponse));
        Mockito.when(restTemplate.getForEntity(anyString(), any()))
                .thenReturn(ResponseEntity.ok(collectResponse));

        assertTrue(bankIdClient.bankIdAuthentication(authRequest));
    }
}

And the class Im testing looks like this:

package se.kt.client;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import se.kt.common.domain.AbstractApplicationForm;
import se.kt.common.vo.PublicApplicationForm;
import se.kt.models.BankIdAuthRequest;
import se.kt.models.BankIdAuthResponse;
import se.kt.models.BankIdCollectResponse;

import javax.validation.constraints.AssertFalse;
import javax.validation.constraints.AssertTrue;
import java.util.Objects;

@Component
public class BankIdClient {

    private static final Logger log = LoggerFactory.getLogger(BankIdClient.class);
    private final RestTemplate customRestTemplate;
    private static final String CONTENT_TYPE = "Content-Type";

    @Value("${BankId.AuthUrl}")
    private String bankIdAuthUrl;

    @Value("${BankId.CollectUrl}")
    private String bankIdCollectUrl;

    @Value("${BankId.SecretKey}")
    private String bankIdSecretKey;

    public BankIdClient(RestTemplate customRestTemplate) {
        this.customRestTemplate = customRestTemplate;
    }

    public BankIdAuthRequest authRequestFromApplicationForm(PublicApplicationForm form, String jobId) {
        BankIdAuthRequest bankIdAuthRequest = new BankIdAuthRequest();
        bankIdAuthRequest.setPno(form.getSsn());
        bankIdAuthRequest.setIpAddress(form.getIp_address());
        bankIdAuthRequest.setRefID(jobId);
        bankIdAuthRequest.setSecretKey(bankIdSecretKey);
        bankIdAuthRequest.setAvsikt("Kt application");

        return bankIdAuthRequest;
    }

    public boolean bankIdAuthentication(BankIdAuthRequest bankIdAuthRequest) throws InterruptedException {
        //Setup header and body for request.
        HttpHeaders headers = new HttpHeaders();
        headers.add(CONTENT_TYPE, MediaType.APPLICATION_JSON.toString());

        ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();

        try {
            String bankIdAuthFormJson = ow.writeValueAsString(bankIdAuthRequest);

            HttpEntity<String> httpEntity = new HttpEntity<>(bankIdAuthFormJson, headers);
            ResponseEntity<BankIdAuthResponse> authResponse = customRestTemplate.postForEntity(bankIdAuthUrl, httpEntity, BankIdAuthResponse.class);

            bankIdCollectUrl += Objects.requireNonNull(authResponse.getBody()).getOrderRef();

            ResponseEntity<BankIdCollectResponse> collectResponse;

            do {
                collectResponse = customRestTemplate.getForEntity(bankIdCollectUrl, BankIdCollectResponse.class);

                Thread.sleep(1500);

                if (Objects.requireNonNull(collectResponse.getBody()).getStatus().equals("completed"))
                    return true;
                if (Objects.requireNonNull(collectResponse.getBody()).getStatus().equals("failed"))
                    return false;
            } while (Objects.requireNonNull(collectResponse.getBody()).getStatus().equals("progress"));

        } catch (JsonProcessingException e) {
            log.info(e.getMessage());
        } catch (NullPointerException e) {
            log.info(e.toString());
            log.info("BankId API not responding correctly. Check server connection");
        }
        return false;
    }

    public void cancelBankIdAuthentication(@Value("${BankId.CancelUrl}") String bankIdCancelUrl) {
        customRestTemplate.postForEntity(bankIdCancelUrl, null, String.class);
    }
}

Now for some reson this line:

ResponseEntity<BankIdAuthResponse> authResponse = customRestTemplate.postForEntity(bankIdAuthUrl, httpEntity, BankIdAuthResponse.class);

keeps producing the result authResponse = null indicating that

Mockito.when(restTemplate.postForEntity(anyString(), any(HttpEntity.class), any()))
                .thenReturn(ResponseEntity.ok(authResponse));

is not working. Now I have been playing around with this for over 3h and i still get the same result. What could i be doing wrong?

Mr.Gomer
  • 501
  • 5
  • 14

1 Answers1

0

@InjectMocks only works with @Mock-annotated fields, not with fields which get assigned a manually created mock. Therefore you need to change:

private final RestTemplate restTemplate = Mockito.mock(RestTemplate.class);

@InjectMocks
private BankIdClient bankIdClient;

to

@Mock
private RestTemplate restTemplate;

@InjectMocks
private BankIdClient bankIdClient;

I also recommend reading Why is my class not calling my mocked methods in unit test? which provides additional insights and points out some common mistakes when using Mockito or mocks in general.

knittl
  • 246,190
  • 53
  • 318
  • 364