I am writing Junits for my spring boot code. I am unable to mock and test RestTemplate. I went with SpringBootTest as an integration test approach instead of simple unit test, as the resttemplate was not getting initialized:
This is my client layer:
@Component
class PromoClient(
@Autowired private val restTemplate: RestTemplate) {
@Value("\${billing.url}")
private lateinit var billingUrl: String
fun getPromotion(): PromotionsDTO {
val response = restTemplate.getForEntity<PromotionsDTO>(billingUrl)
return response.body?: throw IOException("No Promocode available")
}
}
Trying these Junits which are failing:
import com.ninjasquad.springmockk.MockkBean
import com.webapp.util.PromotionsDTO
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import org.mockito.ArgumentMatchers.any
import org.mockito.Mock
import org.mockito.Mockito.anyString
import org.mockito.Mockito.`when`
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.mock.mockito.MockBean
import org.springframework.boot.test.web.client.TestRestTemplate
import org.springframework.boot.test.web.client.getForEntity
import org.springframework.context.annotation.Bean
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.test.context.TestConstructor
import org.springframework.web.client.RestTemplate
import java.net.URI
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
@TestConstructor(autowireMode = TestConstructor.AutowireMode.ALL)
class PromoClientTest {
@Autowired
private lateinit var promoClient: PromoClient
@Mock
private lateinit var restTemplate: TestRestTemplate
@Bean
open fun restTemplate(): TestRestTemplate {
return TestRestTemplate()
}
@Test
fun testGetPromotion() {
val billingUrl = "https://example.com/promotions"
val expectedPromotion = PromotionsDTO("TEST PROMO")
// Mock the behavior of RestTemplate
`when`(restTemplate.getForEntity<PromotionsDTO>(anyString(),any()))
.thenReturn(ResponseEntity(expectedPromotion, HttpStatus.OK))
// Test the getPromotion method
val result = promoClient.getPromotion()
// Assert the result
assertEquals(expectedPromotion, result)
}
}
The error is:
I/O error on GET request for "http://localhost:8080/v1/pr": Failed to connect to localhost/[0:0:0:0:0:0:0:1]:8080
org.springframework.web.client.ResourceAccessException: I/O error on GET request for "http://localhost:8080/v1/pr": Failed to connect to localhost/[0:0:0:0:0:0:0:1]:8080
What am i doing wrong ?
Expecting the test cases to pass
Inside your PromoClient you're autowiring a RestTemplate but inside your test you just initialize a TestRestTemplate. Reading the JavaDoc of TestRestTemplate it says:
Note: To prevent injection problems this class intentionally does not extend RestTemplate. If you need access to the underlying RestTemplate use getRestTemplate().
This means that your TestRestTemplate will never be injected into your PromoClient. For testing a client using a RestTemplate you may want to use the @RestClientTest annotation. This would require you to inject the RestClientBuilder inside your PromoClient where you can just call .build() on to get your pre-configured RestTemplate.