0

I have a spring mvc application. There is a class A as shown below:

public class A extends X {

    private final RetryRegistry retryReg;

    @value("${retry.attempts:5}")
    private int retryAttempts;

    @value("${retry.delay:5000}")
    private int retryDelay;

    public A( B b, C c, D d){
        super(b,c,d);
        
        this.retryReg = RetryRegistry.of(RetryConfig.custom()
                         .maxAttempts(this.retryAttempts)   
                         .waitDuration(Duration.ofMillis(this.retryDelay))
                         .build());
    }
    
    .......
}

The test class for class A is as shown below:

@PrepareForTest(A.class)
@runWith(PowerMockRunner.class)
@SupressWarnings("unchecked")
public class ATest {

    @InjectMocks
    private A a;

........

}

Now when test class injects mock and calls A's constructor, the value for retryAttempts is 0 inside the constructor, hence an exception is thrown.

It seems that the configurable properties (from the properties file whose path has been mentioned in servlet-context are no being read) when inject mock tries to construct A.

I tried many things like adding @TestPropertySource to my test file, but it doesn't work.

@TestPropertySource(locations="classpath:SettingsTest.properties")
@PrepareForTest(A.class)
@runWith(PowerMockRunner.class)
@SupressWarnings("unchecked")
public class ATest {

    @InjectMocks
    private A a;

........

}

Am I doing something wrong, or is @TestPropertySource is only supported for Springboot apps. Any suggestions on how I can set the configurable properties before @InjectMocks tries to access class A'constructor.

Akash Sharma
  • 330
  • 3
  • 18
  • The `@ConfigurationProperties` are supposed to be attached to classes with members representing the config values, and in the operational code, not the test code. Have you read a tutorial on them? – daniu Jul 19 '20 at 17:29
  • @daniu the accepted answer in this thread uses it in test class https://stackoverflow.com/questions/31745168/how-to-test-classes-with-configurationproperties-and-autowired – Akash Sharma Jul 19 '20 at 17:30
  • @daniu: thanks for pointing out, I mentioned the wrong annotation – Akash Sharma Jul 19 '20 at 17:33
  • ConfigurationProperties are the solution to your problem though, you can easily mock them. Or do you need the values in a file? Not the best idea. – daniu Jul 19 '20 at 17:39

1 Answers1

0

How about you create the registry as a bean in a @Configuration?

// this will automatically be initialized with yours properties
@ConfigurationProperties(prefix="retry)
class RetryProperties {
    private int attempts;
    private int delay;
}
@Configuration
class RetryConfiguration {
    @Bean RetryRegistry registry(RetryProperties props) {
        return RetryRegistry.of(RetryConfig.custom()
                 .maxAttempts(retryProperties)
                 .waitDuration(Duration.ofMillis(this.retryDelay))
                 .build());
    }
}
@Component // I assume, otherwise the @Value properties wouldn't work
public class A extends X {
    private final RetryRegistry retryReg;

    public A( B b, C c, D d, RetryRegistry reg){
        super(b,c,d);
        this.retryReg = reg;
    }
    .......
}

Now you can mock and inject the registry.

Overall, your structure looks a bit all over the place, with some constructor autowiring, some initialization in the constructor, some fields read from properties. You should probably stick to one of them. Also super(b,c,d) looks pretty suspicious, I've rarely seen a class hierarchy be a good idea for components (with three parameters no less).

daniu
  • 14,137
  • 4
  • 32
  • 53