title: Spring Boot testing: Cannot bind @ConfigurationProperties - Ensure that @ConstructorBinding has not been applied
body: <p>In a Spring Boot unit test, how can you mock @ConstructorBinding @ConfigurationProperties data class?</p>
  <h3>Setup</h3>
  <ul>
  <li>Both
  <ul>
  <li>Kotlin 1.4.30 (for unit tests and config classes)</li>
  <li>Java 15 (with --enable-preview) (for business logic)</li>
  </ul>
  </li>
  <li>Spring Boot 2.4.2</li>
  <li>Junit 5.7.1</li>
  <li>Mockito (mockito-inline) 3.7.7</li>
  <li>Maven 3.6.3_1</li>
  </ul>
  <p>I want to test FtpService (a <code>@Service</code>, which has a <code>RestTemplate</code>) with different configurations.</p>
  <p>Properties for the FtpService come from a Kotlin data class - UrlProperties - which is annotated with <code>ConstructorBinding</code> and <code>@ConfigurationProperties</code>.</p>
  <p>Note: FtpService's constructor extracts a property from UrlProperties. This means that UrlProperties must be both mocked <strong>and</strong> stubbed <strong>before</strong> Spring loads FtpService</p>
  <h3>Error</h3>
  <p>When I try and mock UrlProperties so that I can set the properties for different tests, I either receive an error, or am unable to insert the bean</p>
  <pre><code>Cannot bind @ConfigurationProperties for bean 'urlProperties'. Ensure that @ConstructorBinding has not been applied to regular bean
  </code></pre>
  <hr />
  <h3>Code</h3>
  
    `@SpringBootTest` of FtpService | `src/test/kotlin/com/example/FtpServiceTest.kt`
  <p>
  <pre class="lang-java prettyprint-override"><code>import com.example.service.FtpService
  import com.example.service.UrlProperties
  import org.junit.jupiter.api.Assertions.assertEquals
  import org.junit.jupiter.api.Test
  import org.mockito.Mockito.`when`
  import org.mockito.Mockito.mock
  import org.springframework.beans.factory.annotation.Autowired
  import org.springframework.boot.test.autoconfigure.web.client.AutoConfigureWebClient
  import org.springframework.boot.test.context.SpringBootTest
  import org.springframework.boot.test.context.TestConfiguration
  import org.springframework.boot.test.mock.mockito.MockBean
  import org.springframework.context.annotation.Bean
  import org.springframework.test.context.ContextConfiguration
  
  
  @TestConfiguration
  @SpringBootTest(classes = [FtpService::class])
  @AutoConfigureWebClient(registerRestTemplate = true)
  class FtpServiceTest
  @Autowired constructor(
      private val ftpService: FtpService
  ) {
  
    // MockBean inserted into Spring Context too late,
    // FtpService constructor throws NPE
  //  @MockBean
  //  lateinit var urlProperties: UrlProperties
  
    @ContextConfiguration
    class MyTestContext {
  
      // error -
      // &gt; Cannot bind @ConfigurationProperties for bean 'urlProperties'.
      // &gt; Ensure that @ConstructorBinding has not been applied to regular bean
      var urlProperties: UrlProperties = mock(UrlProperties::class.java)
  
      @Bean
      fun urlProperties() = urlProperties
  
      // error -
      // &gt; Cannot bind @ConfigurationProperties for bean 'urlProperties'.
      // &gt; Ensure that @ConstructorBinding has not been applied to regular bean
  //    @Bean
  //    fun urlProperties(): UrlProperties {
  //      return UrlProperties(
  //          UrlProperties.FtpProperties(
  //              url = &quot;ftp://localhost:21&quot;
  //          ))
  //    }
    }
  
    @Test
    fun `test fetch file root`() {
  
      `when`(MyTestContext().urlProperties.ftp)
          .thenReturn(UrlProperties.FtpProperties(
              url = &quot;ftp://localhost:21&quot;
          ))
  
      assertEquals(&quot;I'm fetching a file from ftp://localhost:21!&quot;,
          ftpService.fetchFile())
    }
  
    @Test
    fun `test fetch file folder`() {
  
      `when`(MyTestContext().urlProperties.ftp)
          .thenReturn(UrlProperties.FtpProperties(
              url = &quot;ftp://localhost:21/user/folder&quot;
          ))
  
      assertEquals(&quot;I'm fetching a file from ftp://localhost:21/user/folder!&quot;,
          ftpService.fetchFile())
    }
  }
  </code></pre>
  </p>
  
  <h3>Workaround - manual definition every test</h3>
  <p>The only 'workaround' is to manually define all beans (which means I miss out on Spring Boot magic during testing) and in my view is more confusing.</p>
  
    Workaround - manual redefinition each test | `src/test/kotlin/com/example/FtpServiceTest2.kt`
  <p>
  <pre class="lang-java prettyprint-override"><code>import com.example.service.FtpService
  import com.example.service.UrlProperties
  import org.junit.jupiter.api.Assertions.assertEquals
  import org.junit.jupiter.api.BeforeEach
  import org.junit.jupiter.api.Test
  import org.mockito.Mockito
  import org.mockito.Mockito.`when`
  import org.mockito.Mockito.doReturn
  import org.mockito.Mockito.mock
  import org.springframework.boot.web.client.RestTemplateBuilder
  
  
  class FtpServiceTest2 {
  
    private val restTemplate =
        RestTemplateBuilder()
            .build()
  
    private lateinit var ftpService: FtpService
  
    private lateinit var urlProperties: UrlProperties
  
    @BeforeEach
    fun beforeEachTest() {
      urlProperties = mock(UrlProperties::class.java)
  
      `when`(urlProperties.ftp)
          .thenReturn(UrlProperties.FtpProperties(
              url = &quot;default&quot;
          ))
  
      ftpService = FtpService(restTemplate, urlProperties)
    }
  
  
    /** this is the only test that allows me to redefine 'url' */
    @Test
    fun `test fetch file folder - redefine`() {
  
      urlProperties = mock(UrlProperties::class.java)
      `when`(urlProperties.ftp)
          .thenReturn(UrlProperties.FtpProperties(
              url = &quot;ftp://localhost:21/redefine&quot;
          ))
      // redefine the service
      ftpService = FtpService(restTemplate, urlProperties)
  
      assertEquals(&quot;I'm fetching a file from ftp://localhost:21/redefine!&quot;,
          ftpService.fetchFile())
    }
    
    
    @Test
    fun `test default`() {
      assertEquals(&quot;I'm fetching a file from default!&quot;,
          ftpService.fetchFile())
    }
  
    @Test
    fun `test fetch file root`() {
  
      `when`(urlProperties.ftp)
          .thenReturn(UrlProperties.FtpProperties(
              url = &quot;ftp://localhost:21&quot;
          ))
  
      assertEquals(&quot;I'm fetching a file from ftp://localhost:21!&quot;,
          ftpService.fetchFile())
    }
  
    @Test
    fun `test fetch file folder`() {
  
      doReturn(
          UrlProperties.FtpProperties(
              url = &quot;ftp://localhost:21/user/folder&quot;
          )).`when`(urlProperties).ftp
  
      assertEquals(&quot;I'm fetching a file from ftp://localhost:21/user/folder!&quot;,
          ftpService.fetchFile())
    }
  
    @Test
    fun `test fetch file folder - reset`() {
  
      Mockito.reset(urlProperties)
      `when`(urlProperties.ftp)
          .thenReturn(UrlProperties.FtpProperties(
              url = &quot;ftp://localhost:21/mockito/reset/when&quot;
          ))
  
      assertEquals(&quot;I'm fetching a file from ftp://localhost:21/mockito/reset/when!&quot;,
          ftpService.fetchFile())
    }
  
    @Test
    fun `test fetch file folder - reset &amp; doReturn`() {
  
      Mockito.reset(urlProperties)
      doReturn(
          UrlProperties.FtpProperties(
              url = &quot;ftp://localhost:21/reset/doReturn&quot;
          )).`when`(urlProperties).ftp
  
      assertEquals(&quot;I'm fetching a file from ftp://localhost:21/reset/doReturn!&quot;,
          ftpService.fetchFile())
    }
  }
  </code></pre>
  </p>
  
  
    Spring App | `src/main/kotlin/com/example/MyApp.kt`
  <p>
  <pre class="lang-java prettyprint-override"><code>package com.example
  
  import org.springframework.boot.autoconfigure.SpringBootApplication
  import org.springframework.boot.context.properties.ConfigurationPropertiesScan
  import org.springframework.boot.context.properties.EnableConfigurationProperties
  import org.springframework.boot.runApplication
  
  @SpringBootApplication
  @EnableConfigurationProperties
  @ConfigurationPropertiesScan
  class MyApp
  
  fun main(args: Array&lt;String&gt;) {
    runApplication&lt;MyApp&gt;(*args)
  }
  </code></pre>
  </p>
  
  
    Example @Service | `src/main/kotlin/com/example/service/FtpService.kt` 
  <p>
  <pre class="lang-java prettyprint-override"><code>package com.example.service
  
  import org.springframework.stereotype.Service
  import org.springframework.web.client.RestTemplate
  
  @Service
  class FtpService(
      val restTemplate: RestTemplate,
      urlProperties: UrlProperties,
      val ftpProperties: UrlProperties.FtpProperties = urlProperties.ftp
  ) {
  
    fun fetchFile(): String {
      println(restTemplate)
      return &quot;I'm fetching a file from ${ftpProperties.url}!&quot;
    }
  }
  </code></pre>
  </p>
  
  
    @ConfigurationProperties with @ConstructorBinding -  `src/main/kotlin/com/example/service/UrlProperties.kt`
  <p>
  <pre><code>package com.example.service
  
  import org.springframework.boot.context.properties.ConfigurationProperties
  import org.springframework.boot.context.properties.ConstructorBinding
  
  @ConstructorBinding
  @ConfigurationProperties(&quot;url&quot;)
  data class UrlProperties(val ftp: FtpProperties) {
  
    data class FtpProperties(
        val url: String,
    )
  }
  </code></pre>
  </p>