1

I am trying with @ConfigurationProperties and I find two things:

  • for tests to work, I have to put configuration properties files (yml) in test package; setters and an empty constructor are required.
  • If I annotated the class with @Validated, injection just fails with all null values.

If you say the first one is understandable, and the second one? Why?

The purpose is to validate configuration properties injection, so that crucial properties values should not be absent when the application launches.

I have this yaml to map:

accertify:
  fr:
    merchant1:
      host: "sandbox.accertify.dev"
      protocol: "https"
      user: "fraud"
      password: "temporal"
      merchant: "merchant1FR"
    merchant2:
      host: "sandbox.accertify.dev"
      protocol: "https"
      user: "fraud"
      password: "temporal"
      merchant: "merchant2FR"
  es:
    merchant1:
      host: "sandbox.accertify.dev"
      protocol: "https"
      user: "fraud"
      password: "temporal"
      merchant: "merchant1ES"
    merchant2:
      host: "sandbox.accertify.dev"
      protocol: "https"
      user: "fraud"
      password: "temporal"
      merchant: "merchant2ES"

I have this class to mapping nested configurations:

import com.westerngun.fraud.jfps.dto.provider.accertify.country.ConfigurationEs;
import com.westerngun.fraud.jfps.dto.provider.accertify.country.ConfigurationFr;
import com.westerngun.fraud.jfps.service.client.ConnectionDetailsInterface;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.validation.annotation.Validated;

import javax.validation.Valid;

/**
 * Class to read Accertify provider connection credentials.
 */
@Getter
@Setter
@Configuration
@ConfigurationProperties(prefix = "accertify")
@Validated   // <--- this seems the problem
@NoArgsConstructor
@AllArgsConstructor
public class AccertifyConnectionDetail extends ConnectionDetails {

    @Valid
    private ConfigurationFr fr;

    @Valid
    private ConfigurationEs es;
}

And, its interface:

public abstract class ConnectionDetails {
    public abstract ConfigurationFr getFr();
    public abstract ConfigurationEs getEs();
}

And, ConfigurationEs and ConfigurationFr are both subclass of:

@Component
public class CountryConfigDetail {
    @Getter
    @Setter
    @Valid
    private Merchant1 merchant1;

    @Getter
    @Setter
    @Valid
    private Merchant2 merchant2;
}

Merchant1 and Merchant2 are subclass of :

@Data
@Component
public class MerchantConfigDetail {

    @NotEmpty
    @NotNull
    private String user;

    @NotEmpty
    @NotNull
    private String password;

    ...
    @NotEmpty
    @NotNull
    private Map<String, String> headers;
}

When I try to inject into some @Autowired tests without @Validated in the first class, it works(fr and es have values in the yml file)

enter image description here

When I add @Validated, fr and es are null.

Test:

@RunWith(SpringRunner.class)
@SpringBootTest
public class AccertifyConfigurationTest {

    @Autowired
    private AccertifyConnectionDetail accertifyConnectionDetail; // <--- this field should be injected


    @Test
    public void givenAAccertifyClientService_WhenClientIsBuild_ShouldHaveTheProviderConfigurationCorrect() {
        AccertifyConfiguration accertifyConfiguration = new AccertifyConfiguration(accertifyConnectionDetail);
        accertifyConfiguration.buildConfiguration("FR", "Merchant1");

        Assert.assertEquals("fraud", accertifyConfiguration.getUser());
        Assert.assertEquals("temporal", accertifyConfiguration.getPassword());
        Assert.assertTrue(accertifyConfiguration.getHost().startsWith("sandbox.accertify."));
        Assert.assertEquals("merchant1FR", accertifyConfiguration.getMerchantCode());
    }
...
WesternGun
  • 11,303
  • 6
  • 88
  • 157
  • 2
    Please add some code for this question, doesn't see correct from my usage. – Darren Forsythe Apr 02 '19 at 09:48
  • Add your test as code not as images. You are using proxies so you are probably doing things you shouldn't be doing in the first place. Next to that you probably don't want `@Configuration` on a `@ConfigurationProperties` class. – M. Deinum Apr 02 '19 at 13:06
  • 1
    I change `@Configuration` to `@Component` and the proxy turns into real objects. And with `@Validated` it still holds all null. About `@Configuration` and `@ConfigurationProperties` you are right. – WesternGun Apr 02 '19 at 13:19
  • Ofcourse they are `null` as you aren't creating them. The framework will not create a new instance, you will have to do that yourself when initializing the field. Check the [`ServerProperties`](https://github.com/spring-projects/spring-boot/blob/master/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java) which instantiates the nested properties before hand. Only fields for which a converter or editor exists will be created, others, for binding, you have to create yourself. – M. Deinum Apr 02 '19 at 13:33
  • What I meant was, without `@Validated`, when I debug into these lines, `accertifyConnectionDetail` is `@Autowired` correctly, the values are read from yaml files and they hold non-null, as the image shows, because Spring knows it is `@ConfigurationProperties`; but, only by un-commenting `@Validated`, autowiring does not work. – WesternGun Apr 04 '19 at 08:01
  • I face the same problem: trying to inject a `@ConfigurationProperties` bean in a `@Configuration` class. Configuration properties correctly set ... until I annotate it with `@Validated`. What boot version are you using ? – ch4mp Jun 10 '19 at 15:28
  • I am with 2.1.1.RELEASE – WesternGun Jun 11 '19 at 10:47
  • it seems that this issue still exist https://github.com/spring-projects/spring-boot/issues/8173 have you found a workaround – Mr.Q Oct 01 '19 at 15:10
  • No, I don't have solution yet. For now I don't use `@Validated`. – WesternGun Oct 01 '19 at 15:58

0 Answers0