spring-boot unit testing get application properties - spring-boot

I'm new to spring-boot, currently trying to develop a kafka producer
I want to test method that use value define in properties file. but it show value is null how solve this.I have added my property files to separate resource file in test folder also
this is my folder structure
#SpringBootTest
public class KafkaProducerImplTest {
#BeforeEach
void setUp() {
}
#Test
void check() {
KafkaProducerImpl kpi = new KafkaProducerImpl();
kpi.check();
}
}
#Service
public class KafkaProducerImpl implements KafkaProducerInterface
{
#Value("${kafka.brokers.local}")
private String kafkaBrokers;
#Value("${schema-registry}")
private String schemaRegistry;
private Properties config()
{
Properties props = new Properties();
props.setProperty("bootstrap.servers",kafkaBrokers);
props.setProperty("acks", "1");
props.setProperty("reties", "10");
props.setProperty("key.serializer", StringSerializer.class.getName());
props.setProperty("value.serializer",Serializer.class.getName());
props.setProperty("schema.registry.url",schemaRegistry);
return props;
}
public <K,T>KafkaProducer<K,T> getProducer()
{
return new KafkaProducer<>(config());
}
public <T>ProducerRecord createRecord(String Topic,T msg)
{
return new ProducerRecord<>(
Topic,msg
);
}
public void sendMessage(KafkaProducer producer,ProducerRecord record)
{
producer.send(record, (recordMetadata, e) -> {
if (e == null){
System.out.println("success");
}
});
producer.flush();
}
public void closeProducer(KafkaProducer producer){
producer.close();
}
public void check(){
System.out.println(schemaRegistry);
}
}

finally i find way, thanks everyone helping me.
#RunWith(SpringRunner.class)
#SpringBootTest
public class KafkaProducerImplTest {
#Autowired
private KafkaProducerInterface kpi;
#Test
public void check() {
kpi.check();
}
}

Annotate you test class with #RunWith(SpringRunner.class) which will load application context for you and instantiate the spring beans. To add spring boot support add #SpringBootTest(Which you already have).
You'll have to remove this line
"KafkaProducerImpl kpi = new KafkaProducerImpl();"
and autowire using interface reference instead. Something like this:
#Autowired
KafkaProducerInterface kpi;
I'm assuming you have the properties used here(ex. "kafka.brokers.local") defined in your test properties file.

Related

Injecting spring bean (service layer class) into ResourceBundle

I created a class using ResourceBundle interface as shown below. This class is dependent on another class. The implementation class for ResourceBundle (QuestionnaireSource as shown below) always has null as dependencies. No matter if I use setter or constructor injection.
Could someone please help me with this issue. I am I missing some configuration here.
#Component
public class QuestionnaireSource extends ResourceBundle {
private final QuestionnaireCache questionnaireCache;
private static final Object lockObject = new Object();
#Override
protected Object handleGetObject(String key) {
// Gets an object for the given key from this resource bundle.
// Returns null if this resource bundle does not contain an object for the given key 0
Object value = null;
try {
value = getString(key, LocaleContextHolder.getLocale());
} catch (Exception ignored) {
}
return value;
}
public Questionnaire getString(String key, Locale locale) {
Locale l = safeLocale(locale);
return getResources(l).get(key);
}
private Locale safeLocale(Locale l) {
if (l.getLanguage().equalsIgnoreCase("DE")) {
return Locale.GERMAN;
} else {
return Locale.ENGLISH;
}
}
protected Map<String, Questionnaire> getResources(Locale locale) {
synchronized (lockObject) {
return questionnaireCache.getQuestionnaireCache().get(locale.getLanguage().toUpperCase());
}
}
#Override
public Enumeration<String> getKeys() {
return null;
}
public QuestionnaireSource(QuestionnaireCache questionnaireCache) {
super();
this.questionnaireCache = questionnaireCache;
}
}
Update:
I found that even simple dependency injection in resourceBundle is failing.
UPdate2:
The way I am using in the main class is as follows:
// ResourceBundle test here
System.out.println("Test here for resource bundle");
Locale locale = new Locale("de", "DE");
ResourceBundle bundle = ResourceBundle.getBundle("com.app.util.QuestionnaireSource", locale);
System.out.println(bundle.getString("some.test.string"));
Update3
I am writing a simple example to convey the scenario:
Some service class
#Service
public class SomeServiceTest {
public String testMethod(){
return "test here and complete";
}
}
Some example implementation of resource bundle
#Component
public class MyResourceBundle extends ResourceBundle {
private final SomeServiceTest someServiceTest;
#Autowired
public MyResourceBundle(SomeServiceTest someServiceTest) {
this.someServiceTest = someServiceTest;
}
#Override
protected Object handleGetObject(String key) {
if(key.equals("test"))
return "test";
return null;
}
#Override
public Enumeration<String> getKeys() {
return null;
}
}
Main.java
main(){
// ResourceBundle test here
System.out.println("Test here for resource bundle");
Locale locale = new Locale("de", "DE");
ResourceBundle bundle = ResourceBundle.getBundle("com.app.util.MyResourceBundle", locale);
System.out.println(bundle.getString("test"));
}
Update4:
I changed the annotation on classes as mentioned by on this post https://www.baeldung.com/spring-inject-bean-into-unmanaged-objects
but still I have the null dependency injection for SomeServiceTest class. The changes are as shown below.
SomeServiceTest.java
#Service
public class SomeServiceTest {
public String testMethod(){
return "test here and complete";
}
}
MyResourceBundle.java
#Configurable
public class MyResourceBundle extends ResourceBundle {
#Autowired
private SomeServiceTest someServiceTest;
public MyResourceBundle() {
}
#Override
protected Object handleGetObject(String key) {
if(key.equals("test"))
return someServiceTest.testMethod();
return null;
}
#Override
public Enumeration<String> getKeys() {
return null;
}
}
still SomeServiceTest class is null.
Can you please post an example on how you are using this class? Is it you (your code) or spring who instanciate it (on startup)?
#Component only works for beans which Spring instanciate. If you want to inject stuff in classes you instanciate in you code you can annotate the class with #Configurable.
Please see https://www.baeldung.com/spring-inject-bean-into-unmanaged-objects for some examples.
Make sure you have initialized the spring context
If you are using spring boot
You can get the application context after it starts and use it to get the bean you want
For example
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(YouApplication.class, args);
MyResourceBundle resConfig = run.getBean("myResourceBundle", MyResourceBundle .class);
resConfig.handleGetObject("test");
}
Unfortunately ResourceBundle.getBundle does not initialize the spring application context

Comparison of Guice and(move to) Spring

Could someone give me advice, please, how to re-write some method using simple Spring (w/o Boot)?
Here I have some code methods:
1. createInjector
private Injector injector;
someMethod(){
injector = Guice.createInjector(new ExampleClass1(), new ExampleClass2());}
2 setModules(Modules.override
setModules(Modules.override(new ExampleClass3()).with(new ExampleClass4()));
//////////////////////////////////////////////////////////////////
public static void setModules(Module... modules) {
initInjector(modules);
}
private static void initInjector(Module... modules) {
injector = Guice.createInjector(modules);
}
}
Taking the risk that my answer is too general.
Roughly saying you can think Guice modules as equivalent a configuration class with #Configuration annotation, that contains #Bean etc.
The Guice injector can be considered as equivalent to the Spring ApplicationContext.
So for example if we have two configuration files:
#Configuration
public class ConfigA {
#Bean
ExampleClass1 exampleClass1(){
return new ExampleClass1();
}
#Bean
ExampleClass2 exampleClass2(){
return new ExampleClass2();
}
}
#Configuration
public class ConfigB {
#Bean
ExampleClass1 exampleClass1(){
return new ExampleClass1();
}
#Bean
ExampleClass3 exampleClass2(){
return new ExampleClass3();
}
}
And Services ExampleClass4 that you want as alternative of ExampleClass3.
You may use the #Primary annotation
public class ExampleClass4 extends ExampleClass3 {
#Override
public String toString() {
return "ExampleClass4{}";
}
}
#Configuration
public class ConfigC {
#Bean
#Primary
ExampleClass3 exampleClass3(){
return new ExampleClass4();
}
}
So rewriting the app to Spring (core 5.2, not Spring boot) will be:
public class App {
public static void main(String[] args) {
AnnotationConfigApplicationContext ap = initAppContext();
overrideBinding(ap);
System.out.println(ap.getBean(ExampleClass3.class));
//prints ExampleClass4{}
}
private static AnnotationConfigApplicationContext initAppContext() {
AnnotationConfigApplicationContext ap = new AnnotationConfigApplicationContext();
ap.register(ConfigA.class, ConfigB.class);
return ap;
}
private static void overrideBinding(AnnotationConfigApplicationContext ap) {
ap.register(ConfigC.class);
ap.refresh();
}
}
This technic of overriding a binding will work only because ExampleClass3 wasn't defined as primary, if it doesn't that would not work and you need to consider a different approach.
For more information:
https://www.baeldung.com/spring-application-context
https://docs.spring.io/spring-javaconfig/docs/1.0.0.m3/reference/html/modularizing-configurations.html
Override bean definition in java config

Spring Boot: Testing custom MongoTemplate converters

I'm using this custom converters into my Spring Boot service:
#Configuration
public class MongoConfig {
#Bean
public MongoCustomConversions customConversions(){
List<Converter<?,?>> converters = new ArrayList<>();
converters.add(ReferenceWriterConverter.INSTANCE);
return new MongoCustomConversions(converters);
}
#WritingConverter
enum ReferenceWriterConverter implements Converter<Reference, DBObject> {
INSTANCE;
#Override
public String convert(Reference reference) {
//do stuff
}
}
}
Into my controllers, I'm using MontoTemplate in order to talk with MongoDB. So, all converters are already loaded into template.
However, I'd like to test MongoDbTemplate using Spring injection features. I mean, I want to test MongoDbTemplate using custom converters which should already be loaded.
Any ideas on how it can be achieved?
EDIT
public class ModelTest {
private List<Reference> references;
public ModelTest() {
this.references = new ArrayList<Reference>();
}
#Before
public void setUp() {
Reference reference = new Reference();
reference.setId("Ref1");
reference.setTimestamp(new Date());
Metadata met = new Metadata();
met.setId("Mdt1");
met.setUser("user");
met.setCreationTimestamp(new Date());
met.setMetadata("[{'departament': 'JUST'}]");
reference.setMetadata(met);
this.references.add(reference);
ServerAddress serverAddress = new ServerAddress("127.0.0.1", 27017);
MongoClient mongoClient = new MongoClient(serverAddress);
MongoTemplate mongoTemplate = new MongoTemplate(mongoClient, "db");
mongoTemplate.insert(reference);
}
/**
* Assert Office mime type documents.
*/
#Test
public void office() {
fail("Not yet implemented");
}
}
EDIT 2
I also would like to use custom testing properties. I mean, currently, we are setting properties into src/test/resources/application.properties.
spring.data.mongodb.host: localhost
spring.data.mongodb.port: 27017
How could I load these file properties?
Solution 1
If you want to test it with the Spring context, you can annotate your Test class as SpringBootTest and autowire the MongoTemplate. This should then contain your custom conversions for you to test them:
#RunWith(SpringRunner.class)
#SpringBootTest
public class ModelTest {
private List<Reference> references;
#Autowired
private final MongoTemplate mongoTemplate;
public ModelTest() {
this.references = new ArrayList<Reference>();
}
#Before
public void setUp() {
Reference reference = new Reference();
reference.setId("Ref1");
reference.setTimestamp(new Date());
Metadata met = new Metadata();
met.setId("Mdt1");
met.setUser("user");
met.setCreationTimestamp(new Date());
met.setMetadata("[{'departament': 'JUST'}]");
reference.setMetadata(met);
this.references.add(reference);
mongoTemplate.insert(reference);
}
/**
* Assert Office mime type documents.
*/
#Test
public void office() {
fail("Not yet implemented");
}
}
Solution 2
If you just want to test the converter alone, you could make a ReferenceWriterConverterTest like so:
public class ReferenceWriterConverterTest {
private ReferenceWriterConverter converter;
#Before
public void setUp() {
converter = ReferenceWriterConverter.INSTANCE;
}
//test stuff
}

Field created in spring component in not initialized with new keyword

I have spring component class annotated with #Component and in it I have field ConcurrentHashMap map, which is init in constructor of component and used in spring stream listener:
#Component
public class FooService {
private ConcurrentHashMap<Long, String> fooMap;
public FooService () {
fooMap = new ConcurrentHashMap<>();
}
#StreamListener(value = Sink.INPUT)
private void handler(Foo foo) {
fooMap.put(foo.id, foo.body);
}
}
Listener handle messages sent by rest controller. Can you tell me why I always got there fooMap.put(...) NullPointerException because fooMap is null and not initialzied.
EDIT:
After #OlegZhurakousky answer I find out problem is with async method. When I add #Async on some method and add #EnableAsync I can't anymore use private modificator for my #StreamListener method. Do you have idea why and how to fix it?
https://github.com/schwantner92/spring-cloud-stream-issue
Thanks.
Could you try using #PostConstruct instead of constructor?
#PostConstruct
public void init(){
this.fooMap = new ConcurrentHashMap<>();
}
#Denis Stephanov
When I say bare minimum, here is what I mean. So try this as a start, you'll see that the map is not null and start evolving your app from there.
#SpringBootApplication
#EnableBinding(Processor.class)
public class DemoApplication {
private final Map<String, String> map;
public static void main(String[] args) {
SpringApplication.run(DemoRabbit174Application.class, args);
}
public DemoApplication() {
this.map = new HashMap<>();
}
#StreamListener(Processor.INPUT)
public void sink(String string) {
System.out.println(string);
}
}
With Spring everything has to be injected.
You need to declare a #Bean for the ConcurrentHashMap, that will be injected in you Component. So create a Configuration class like:
#Configuration
public class FooMapConfiguration {
#Bean("myFooMap")
public ConcurrentHashMap<Long, String> myFooMap() {
return new ConcurrentHashMap<>();
}
}
Then modify your Component:
#Component
public class FooService {
#Autowired
#Qualifier("myFooMap")
private ConcurrentHashMap<Long, String> fooMap;
public FooService () {
}
#StreamListener(value = Sink.INPUT)
private void handler(Foo foo) {
fooMap.put(foo.id, foo.body); // <= No more NPE here
}
}

Retrieving the value of a property pom.xml

I would like to retrieve the value of a property in file application.properties in my service layer of my application, the value of setVersion is null
version=5.4.3
and the function for recovery the version
#Override
public ProductDto getVersionApp() {
ProductDto dto = new ProductDto();
Properties prop = new Properties();
try {
prop.load(new FileInputStream("/concerto-rest-api/src/main/resources/application.properties"));
dto.setVersion(prop.getProperty("version"));
LOG.info("version ",prop.getProperty("version"));
} catch (IOException ex) {}
return dto;
}
You can use #Value("${version}") in you service, provided you service is a spring bean.
If you are using the spring-boot framework, there are several ways you can get that property.
First:
#SpringBootApplication
public class SpringBoot01Application {
public static void main(String[] args) {
ConfigurableApplicationContext context=SpringApplication.run(SpringBoot01Application.class, args);
String str1=context.getEnvironment().getProperty("version");
System.out.println(str1);
}
}
Second:
#Component
public class Student {
#Autowired
private Environment env;
public void speak() {
System.out.println("=========>" + env.getProperty("version"));
}
}
Third:
#Component
#PropertySource("classpath:jdbc.properties")//if is application.properties,then you don't need to write #PropertyScource("application.properties")
public class Jdbc {
#Value("${jdbc.user}")
private String user;
#Value("${jdbc.password}")
private String password;
public void speack(){
System.out.println("username:"+user+"------"+"password:"+password);
}
}

Resources