spring test #Value not being populated - spring

I'm trying to run unit tests in spring-test and I cannot get the #Value to populate in the Injected Classes.. My looks like this.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {
RelationshipCacheFactoryImpl.class,
IgniteBoot.class,
ServerMarker.class})
#TestPropertySource("classpath:test.properties")
public class RelationshipCacheFactoryImplTest {
...
So in my IgniteBoot class I have this
#Component
public class IgniteBoot {
Logger logger = LoggerFactory.getLogger(IgniteBoot.class);
#Autowired
ApplicationContext context;
#Autowired
IgniteClientConfig clientConfig;
#Value("${ignite.tcp.finder:MULTICAST}")
String tcpFinder;
#Value("${ignite.tcp.finder.sharedfs.path:/tmp}")
String fsFinderPath;
#Value("${ignite.name:tempGrid}")
String name;
#Value("${ignite.roles:testRole}")
String roles;
#Value("${ignite.h2Debug:false}")
String h2DebugStr;
...
The #Value annotated Strings are all populated with the values in the $Value String, but not with the actual values from the properties file.
Any Idea what this could be?

You need to add PropertySourcesPlaceholderConfigurer to your test configuration:
#Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
In your test you don't use #EnableAutoConfiguration (separetly or implicitly as part of #SpringBootApplication) which registers this bean for you in the application, so you need to register it yourself.

Related

Spring Boot JUnit tests fail with Status expected:<200> but was:<404>

For some time I've been struggling to make JUnit tests for my rest controller. For some reason, every time I try to run them I get the error Status expected:<200> but was:<404>. Here is my controller:
#RestController
#RequestMapping("/travels")
#RequiredArgsConstructor
public class TravelController {
private final TravelService travelService;
private final TravelOutputDtoMapper travelOutputDtoMapper;
#GetMapping
public List<TravelOutputDto> getAll() {
List<Travel> travels = travelService.getAll();
return travels.stream()
.map(travelOutputDtoMapper::travelToTravelOutputDto)
.collect(Collectors.toList());
}
}
And here is my test:
#ExtendWith(SpringExtension.class)
#WebMvcTest(controllers = TravelController.class)
#ContextConfiguration(classes = {
TravelOutputDtoMapper.class,
TravelOutputDtoMapperImpl.class
})
class TravelControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private TravelService travelService;
#Autowired
private TravelOutputDtoMapper travelOutputDtoMapper;
#Test
void testGetAll() throws Exception {
List<Travel> travels = mockTravelList();
Mockito.when(travelService.getAll()).thenReturn(travels);
mockMvc.perform(get("/travels"))
.andExpect(status().isOk());
}
private List<Travel> mockTravelList() {
// Dummy travel list
}
}
I think the reason is connected with TravelOutputDtoMapper as if I remove it from the controller and don't try to inject it the tests are passing, but I cannot find any information why it is doing it. The autowired mapper has an instance and works just fine.
Here is the Mapper:
#Mapper(componentModel = "spring")
public interface TravelOutputDtoMapper {
#Mapping(target = "from", source = "entity.from.code")
#Mapping(target = "to", source = "entity.to.code")
TravelOutputDto travelToTravelOutputDto(Travel entity);
}
The #ContextConfiguration annotation is used for a different purpose:
#ContextConfiguration defines class-level metadata that is used to determine how to load and configure an ApplicationContext for integration tests.
Using Spring Boot and #WebMvcTest there's no need to manually specify how to load the context. That's done for you in the background.
If you'd use this annotation, you'd specify your main Spring Boot class here (your entry-point class with the #SpringBootApplication annotation).
From what I can see in your test and your question is that you want to provide an actual bean for the TravelOutputDtoMapper, but mock the TravelService.
In this case, you can use #TestConfiguration to add further beans to your sliced Spring TestContext:
// #ExtendWith(SpringExtension.class) can be removed. This extension is already registered with #WebMvcTest
#WebMvcTest(controllers = TravelController.class)
class TravelControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private TravelService travelService;
#Autowired
private TravelOutputDtoMapper travelOutputDtoMapper;
#TestConfiguration
static class TestConfig {
#Bean
public TravelOutputDtoMapper travelOutputDtoMapper() {
return new TravelOutputDtoMapper(); // I assume your mapper has no collaborators
}
}
// ... your MockMvc tests
}

Is it bad to put #Service/#Component along with #Bean?

Suppose I have this service bean:
#Service
public class MyService{
private final HashMap<String,String> values;
...
}
with the values being:
com.foo:
values:
a: world
b: helo
I may want to create it inside of a configuration:
#Configuration
#ConfigurationProperties(prefix="com.foo")
public class MyConf{
private Map<String, String> values;
#Bean
public MyService myService(){
return new MyService(values);
}
}
But I fear that spring could do something strange like creating 2 beans or dunno what...is this a good practice or should I just move #ConfigurationProperties inside of the #Service itself?
You can inject your configuration directly into your Service
#Service
public class MyService{
private final MyConf conf;
public MyService(MyConf conf) {
this.conf = conf;
}
}
And remove the #Bean annotation from MyConf allong with myservice method.
You should not do that, as it will create two beans of the same type.
In your case, you have not mentioned different names for the beans
so it will override if spring.main.allow-bean-definition-overriding=true else it will fail.
PS: For #Service annotation to create a bean, the class package should be configured in the #ComponentScan or in the base scan package
If you want to use your properties values in your Service class (or anywhere else) you should just inject it :
#Service
public class MyService{
#Autowired
private MyConf myConf;
}

Spring #ConfigurationProperties not populated

I am experiencing problems using the #ConfigurationProperties feature.
Probably, I am missing something, since the mechanism seems very simple, but for me, it does not work.
I am using Spring Boot with the following main Application class
#SpringBootApplication
#EnableAspectJAutoProxy
#EnableConfigurationProperties(QueuesProperties.class)
#PropertySource("file:config/queues.properties")
#ImportResource("classpath:/spring-config.xml")
public class Application {
public static void main(String... args) {
ConfigurableApplicationContext ctx = SpringApplication.run(Application.class, args);
}
}
with QueuesProperties
#ConfigurationProperties(prefix = "wmq.in.queue")
public class QueuesProperties {
private static final Logger LOGGER = LoggerFactory.getLogger(QueuesProperties.class);
private String descr;
public String getDescr() {
return descr;
}
public void setDescr(String descr) {
this.descr = descr;
}
}
The properties file is very simple (I am trying to isolate the problem)
wmq.in.queue.descr = description
Then, I am trying to #Autowired the QueuesProperties in a #Component that I use in a spring-integration flow with a .
The QueuesProperties is correctly injected but the descr attribute is null.
#Autowired
private QueuesProperties queuesConfiguration;
while this
#Value("${wmq.in.queue.descr}")
private String descr;
is correctly evaluated.
I have made a lot of attempt with different configurations or code, but the result is the same. I get the QueuesProperties bean but it is not populated.
What am I missing?
Reading the question isn't very clear if the wmq.in.queue.descr = description properties is written in applciation.properties file. I said it because you say that the properties is correctly evaluated with #Value and not with
#Autowired
private QueuesProperties queuesConfiguration;
Even the #PropertySource("file:config/queues.properties") let me to think that probably the your wmq.in.queue.descr = description properties isn't written in applciation.properties but in file:config/queues.properties.
Summing
For use #ConfigurationProperties feature you have write the properties in application.properties and use #EnableConfigurationProperties(QueuesProperties.class) on #Component, #Configuration and so on annotated classes like below.
#Component
#EnableConfigurationProperties(QueuesProperties.class)
public class YourBean {
....
private final QueuesProperties queuesProperties;
public YourBean(QueuesProperties queuesProperties){
this.queuesProperties = queuesProperties;
}
.....
}
actually you can change the application.properties file name customizing spring boot properties evaluation but for your local app I discourage. I consider application.properties a good name for naming a place in which you put the configuration properties of your application
I hope that it can help you

Spring #Bean factory method invoked ahead of #Value variable being resolved

I'm having an issue with the order resolution of Spring Java/XML configuration. It seems that the #Value annotations are not being resolved ahead of #Bean factory methods being invoked, specifically when loading properties from external XML configuration.
This is a condensed version of what I'm doing:
#Configuration
#ImportResource({"classpath:configurable-context.xml"})
public class SecurityConfig {
#Value("#{myProps['my.custom.key']}")
private String someValue = null;
#Bean
public SomeObject someObject() {
return new SomeObject(someValue); // Fails because someValue == null
}
}
and this is configurable-context.xml :
...
<util:map id="myProps">
<entry key="my.custom.key" value="myVal"/>
</util:map>
...
The issue is that the someObject(...) factory method is invoked ahead of the #Value annotation being evaluated for someValue, so this is null at the time.
Any thoughts on how I can force resolution of the someValue variable ahead of the factory method being invoked?
Update
As inspired by response from #Ekem, this code worked for me using XML sourced properties:
#Configuration
#ImportResource({"classpath:configurable-context.xml"})
public class SecurityConfig {
#Resource(name = "myProps")
private Properties myProps;
#Bean
public SomeObject someObject() {
return new SomeObject(myProps.getProperty("my.custom.key")); // Now works :-)
}
}
Change your configuration as follows so that myProps bean is initialized first
#Configuration
#ImportResource({"classpath:configurable-context.xml"})
public class SecurityConfig {
#Value("#{myProps['my.custom.key']}")
private String someValue = null;
#Bean
#DependOn("myProps")
public SomeObject someObject() {
return new SomeObject(someValue);
}
}
Alternatively to make your configuration clean use of the environment abstraction as follows
#Configuration
#PropertySource("classpath:application.properties")
public class SecurityConfig {
#Autowired
private private Environment env;
#Bean
public SomeObject someObject() {
return new SomeObject(env.getProperty("my.custom.key"));
}
}
Then add an application.properties file to the root of your classpath with an entry my.custom.key=myVal
This will eliminate the need for an xml application context just to define a hardcoded property

Define spring property values in Java

I have some spring beans which wire in property values using the #Value annotation.
e.g.
#Value("${my.property}")
private String myField;
Usually the values are sourced from property files.
The test I am currently writing uses a fully annotation based configuration.
e.g.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(loader=AnnotationConfigContextLoader.class)
public class AcceptanceTest implements ApplicationContextInitializer<ConfigurableApplicationContext> {
#Configuration
#ComponentScan(basePackages = {
"my.package.one",
"my.package.two"
})
static class ContextConfiguration {
#Bean
public MyBean getMyBean(){
return new MyBean();
}
}
#Autowired
private AnotherBean anotherBean;
#Test
public void testTest(){
assertNotNull(anotherBean);
. . .
}
. . .
I don't wish to reference an external properties file, as I want to keep everything local to the test.
Is there anyway I can specify in java, values for such properties, so that they will be wired in automatically to any beans which need them.
Any help would be appreciated.
Here's one simple approach:
#Configuration
public class PropertiesConfig {
#Bean
public PropertyPlaceholderConfigurer myConfigurer() {
PropertyPlaceholderConfigurer configurer = new PropertyPlaceholderConfigurer();
Properties props = new Properties();
Map myMap = new HashMap<String, String>();
myMap.put("my.property", "my value");
myMap.put("second.my.property", "another value");
props.putAll(myMap);
configurer.setProperties(props);
return configurer;
}
}
As of Spring Framework 4.1, you can use the #TestPropertySource annotation to declare inlined properties for the ApplicationContext loaded for your tests like this:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
#TestPropertySource(properties = { "foo = bar", "magicNumber: 42" })
public class ExampleTests { /* ... */ }
Consult the Context configuration with test property sources section of the reference manual for details.
Prior to Spring Framework 4.1, the easiest way is to configure a custom PropertySource and register it with the Spring TestContext Framework (before the ApplicationContext is loaded for your test).
You can achieve this by implementing a custom ApplicationContextInitializer and using an org.springframework.mock.env.MockPropertySource like this:
public class PropertySourceInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
public void initialize(ConfigurableApplicationContext applicationContext) {
applicationContext.getEnvironment().getPropertySources().addFirst(
new MockPropertySource().withProperty("foo", "bar"));
}
}
You can then register your initializer for your test like this:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(initializers = PropertySourceInitializer.class)
public class ExampleTests { /* ... */ }
Regards,
Sam (author of the Spring TestContext Framework)
If you are using Spock you can also use #TestPropertySource:
#SpringBootTest
#TestPropertySource(properties = [ "my.test.property = bar", "..." ])
It requires the String array to be in Groovy syntax of course, caught me out. I'm using Spock 1.1

Resources