Excluding Configuration class from SpringBootTest - spring

I have a class to configure Kafka under src/main/java:
#Configuration
public class SenderConfig {
#Value("${spring.kafka.producer.bootstrap-servers}")
private String bootstrapServers;
#SuppressWarnings({ "unchecked", "rawtypes" })
#Bean
public ProducerFactory<String,Item> producerFactory(){
log.info("Generating configuration to Kafka key and value");
Map<String,Object> config = new HashMap<>();
config.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,bootstrapServers);
config.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
config.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JsonSerializer.class);
return new DefaultKafkaProducerFactory(config);
}
I have a class under src/test/java to test a repository and I want to exclude this configuration class:
#SpringBootTest(properties = { "spring.cloud.config.enabled=false",
"spring.autoconfigure.exclude=com.xyz.xyz.config.SenderConfig" })
#Sql({ "/import_cpo_workflow.sql" })
public class WorkflowServiceTest {
#Autowired
private WorkflowRep workflowRep;
#Test
public void testLoadDataForTestClass() {
assertEquals(1, workflowRep.findAll().size());
}
}
Error: Caused by: java.lang.IllegalStateException: The following classes could not be excluded because they are not auto-configuration classes: com.xyz.xyz.config.SenderConfig
How can I exclude this configuration class from my test since I'm not testing Kafka in this moment?

You can declare a SenderConfig property in the test class, annotated as #MockBean (and do nothing with it if you don't need it in the test) and that will effectively override the real one in the test's ApplicationContext and stop the real one from being instantiated by the BeanFactory.
https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/test/mock/mockito/MockBean.html

Try to use #ComponentScan to exclude classes.
Example:
#ComponentScan(basePackages = {"package1","package2"},
excludeFilters = {#ComponentScan.Filter(
type = FilterType.ASSIGNABLE_TYPE,
value = {SenderConfig.class})
})

Related

Do not want to load application.yml when testing with Spring Boot

How can I tell Spring to load only application-test.yml and not application.yml file ?
I have a config class:
#ActiveProfiles("test")
#SpringBootConfiguration
public class MongoTestConfig {
#Autowired
MongoOperations operations;
...
}
And a test class:
#RunWith(SpringRunner.class)
#DataMongoTest
#SpringBootTest(classes = MongoTestConfig.class)
public class TagDefinitionRepositoryTest {
...
#Test
....
}
I've tried to add :
#TestPropertySource(locations = {"classpath:application-test.yml"})
#ContextConfiguration(initializers = ConfigFileApplicationContextInitializer.class)
To my config class but it doesn't work: Spring still load application.yml
I don't think you can tell Spring Boot to ignore application.yml completely. What you can do though is to override all the non desired properties using test specific property files.
Based on the code snippet you posted, any property in application-test.yml will override the equivalent property in application.yml.
Spring Boot considers application-test.yml specific to the profile "test" (which has a higher priority over the default application.yml). No annotation #TestPropertySource is needed.
But if you want to choose another name for your properties file, then you can use #TestProertySource, since files indicated in #TestProperySource parameters have higher priority over the others.
You might want to have a look at Spring Boot external configuration rules for resolving properties
I've end up using #SpringBootTest instead of #DataMongoTest
#SpringBootConfiguration
#ComponentScan(basePackages = {"com.package.services"})
#EnableMongoRepositories(basePackages = {"com.package.repositories"})
public class MongoTestConfig {
private static final MongodStarter starter = MongodStarter.getDefaultInstance();
#Bean
public MongoClient mongoClient() throws IOException {
MongodExecutable _mongodExe;
MongodProcess _mongod;
_mongodExe = starter.prepare(new MongodConfigBuilder()
.version(Version.Main.V3_2)
.net(new Net("localhost", 12345, Network.localhostIsIPv6()))
.build());
_mongod = _mongodExe.start();
MongoClient _mongo = new MongoClient("localhost", 12345);
return _mongo;
}
#Bean
public MongoDbFactory mongoDbFactory() throws IOException{
return new SimpleMongoDbFactory(mongoClient() , "test");
}
#Bean
public MongoTemplate mongoTemplate() throws IOException {
return new MongoTemplate(mongoDbFactory());
}
And my test class is:
#RunWith(SpringRunner.class)
#SpringBootTest
public class MyRepositoryTest {
...

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

spring test #Value not being populated

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.

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

How to configure Services with Spring Data JPA repositories with spring Java Configuration

I am trying to figure out how to get a hold of the OrderRepository so that I can pass it into the constructor of the OrderServiceImpl using Spring's java configuration (I already know how to do it with xml configuration).
#Configuration
#ComponentScan(basePackages = "com.sample.app")
#EnableJpaRepositories("com.sample.app")
#EnableTransactionManagement
public class AppConfig
{
#Bean
public OrderService orderService()
{
return new OrderServiceImpl(orderRepository());
}
#Bean
public OrderRepository orderRepository()
{
return ??? What goes here ???
}
...
}
#Configuration
#ComponentScan(basePackages = "com.sample.app")
#EnableJpaRepositories("com.sample.app")
#EnableTransactionManagement
public class AppConfig {
#Autowired
private OrderRepository orderRepository;
#Bean
public OrderService orderService() {
return new OrderServiceImpl(orderRepository);
}
}
Something like that should work. Or simply put a field inside your OrderServiceImpl which is annotated with #Autowired and remove the constructor which takes an orderRepository. Or rely on component-scanning and remove the #Bean methods all together.
You have a component-scan and #Bean method, you might run into duplicate instances of your service that way (if it is annotated with #Service).

Resources