Search code examples
spring-bootkotlinjunit

Spring boot Junit tests fail : 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


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


Solution

  • 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.