0

I am trying to write an Integration test where I have an issue in mocking the rest call which is calling outside server using JUnit. I have added a @Mock and @InjectMock on service

Service looks like this.

@Service
public class BoundaryDeltaService {
    private BoundaryDelta getBoundaryDeltaUsingApp() {
       List<BoundaryValueInfo> infoFromSource = Arrays.asList(serviceAdapter.readInfoFromApiUsingApp(boundarySet, app, loginUserInfo));
      return  getBoundaryDeltacompareCurrentBoundaryValuesWithSource(boundarySet, infoFromSource );
    }
}

There is another service with has restTemplate call

@Service
public class ServiceAdapter {
    public BoundaryValueInfo[] readInfoFromApiUsingApp(){
        String loginToken = systemUserLoginService.getSystemUserTokenManual(app, loginUserInfo);
        restTemplate = createRestTemplate(boundarySet);
        HttpHeaders headers = new HttpHeaders() {{
          String authHeader = "Bearer " + loginToken;
          set( "Authorization", authHeader );
        }};

        HttpEntity<String> request = new HttpEntity<String>(headers);
        ResponseEntity<BoundaryValueInfo[]> answerFromApi = restTemplate.exchange(boundarySet.getApiUri(), HttpMethod.GET, request,  BoundaryValueInfo[].class);
        return getResponseFromApi(answerFromApi);
    }
}

And this is the test scenario

@RunWith(SpringJUnit4ClassRunner.class)
@ActiveProfiles({"aws", "local"})
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = FlywayConfig.class)
@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)
public class BoundaryValueDeltaControllerTest {

    private static final String API_V1 = "/api/v1/";

    @Autowired
    TestRestTemplate testRestTemplate;

    @Autowired
    private BoundaryDeltaService boundaryDeltaService;

    @Autowired
    private DomainBuilder domainBuilder;

    @Autowired
    private AppBuilder appBuilder;

    @Autowired
    private AppAdminBuilder appAdminBuilder;

    @Autowired
    private BoundaryValueBuilder boundaryValueBuilder;

    @Autowired
    private BoundarySetBuilder boundarySetBuilder;

    @MockBean
    private LoginUserProvider loginUserProvider;
    @MockBean
    private LoginTokenService loginTokenService;
    @InjectMocks
    private BoundaryServiceAdapter boundaryServiceAdapter;

    @Mock
    RestTemplate restTemplate;

    @LocalServerPort
    private int port;

    Domain domain;
    App app;
    BoundarySet boundarySet;
    BoundaryValue boundaryValue;
    LoginUserInfo loggedInUser;

    @Before
    public void setUp() {
        clear();
        domain = domainBuilder.persist();
        app = appBuilder.persist(domain);
        boundarySet =  boundarySetBuilder.persist(domain);
        boundaryValue = boundaryValueBuilder.persist(boundarySet);
    }

    @After
    public void tearDown() {
        clear();
    }

    @BeforeClass
    public static void setupTestEnv() {
        // https://github.com/localstack/localstack/issues/592
    }

    @Test
    public void updateBoundaryValuesFromApi() {
        aLoggedInUser(domain.getAuthor().getUsername());
        appAdminBuilder.persist(app, domain.getAuthor());
        ResponseEntity<BoundaryValueInfo[]> answerFromApi = getBoundaryValueInfos();

        HttpHeaders headers = new HttpHeaders() {{
            String authHeader = "Bearer 1234";
            set( "Authorization", authHeader );
        }};

        HttpEntity<String> request = new HttpEntity<>(headers);
        //when(restTemplate.exchange(boundarySet.getApiUri(), HttpMethod.GET, request,  BoundaryValueInfo[].class)).thenReturn(answerFromApi);

        when(restTemplate.exchange(ArgumentMatchers.anyString(),
                ArgumentMatchers.any(HttpMethod.class),
                ArgumentMatchers.any(),
                ArgumentMatchers.<Class<BoundaryValueInfo[]>>any())
        ).thenReturn(answerFromApi);

        String url = url(API_V1 + "domains/" + domain.getName() + "/boundarysets/" + boundarySet.getBoundarySetName() + "/app/" + app.getName()+ "/updatefromapi/");
        ResponseEntity<String> response = testRestTemplate.exchange(url,HttpMethod.GET, null, String.class);

        assertEquals(HttpStatus.OK, response.getStatusCode());
    }
}

I am calling controller with api and from there it is going into above services which has rest call but not able to mock the actual call. Can someone guide me here ?

SamD
  • 185
  • 5
  • 22
  • `restTemplate` in `ServiceAdapter ` is not autowired. It is being created using `createRestTemplate(boundarySet);`. You can't directly inject a mock if your implementation is like this. You can refactor your code and move `createRestTemplate(boundarySet);` to separate class and mock this class in the junit, – Smile May 07 '20 at 04:56
  • @Smile it's like, I can't mock the `ServiceAdapter` because it is integration test and I have to autowire the RestTemplate as well. my `createRestTemplate(boundarySet)` doing nothing much but looks like this. Would it be relevant to move it in total separate call ? `private RestTemplate createRestTemplate(BoundarySet boundarySet) { return new RestTemplate(); }` – SamD May 07 '20 at 06:05
  • Move `createRestTemplate()` to separate class like 'RestTemplateFactory' and then mock `RestTemplateFactory` in your junit while the `ServiceAdapter` will have `RestTemplateFactory` as `@Autowired`. – Smile May 07 '20 at 09:19
  • @Smile Okay, But the issue is when you call the restTemplate.exchange(). How do I manage to mock that in when().thenReurn() ? – SamD May 07 '20 at 10:06
  • You can mock `restTemplateFactory.createRestTemplate()` to return mocked rest template object – Smile May 07 '20 at 11:57
  • Yeah, I tried that but didn't work as expected. I made my work possible by using mock server. seems rather easy in implementation. – SamD May 07 '20 at 12:07

1 Answers1

0

You are not using the TestRestTemplate for the purpose it is intended for.

TestRestTemplate is not an extension of RestTemplate, but rather an alternative that simplifies integration testing and facilitates authentication during tests. It helps in customization of Apache HTTP client, but also it can be used as a wrapper of RestTemplate

Solution:

Approach1: 1) Directly call the exchange method using RestTemplate object.

    ResponseEntity<String> response = restTemplate .exchange(url,HttpMethod.GET, null, String.class);

2) Mock the testRestTemplate and do the mock when on the exchange method call for this object.

    when(testRestTemplate.exchange(ArgumentMatchers.anyString(),
            ArgumentMatchers.any(HttpMethod.class),
            ArgumentMatchers.any(),
            ArgumentMatchers.<Class<BoundaryValueInfo[]>>any())
    ).thenReturn(answerFromApi);
  • How will I call it directly ? It's an integration test and we can't break the flow like this. calling it separate will not fall in in integration test. – SamD May 07 '20 at 06:14