ContextConfiguration does not inject config file when running the Test - spring

I have this configuration class in a maven project:
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import lombok.Data;
#Data
#Configuration
public class SmsConfig {
#Value("${sms.domainId}")
private String domainId;
#Value("${sms.gateway.url}")
private String gatewayUrl;
#Value("${sms.cmd}")
private String cmd;
#Value("${sms.login}")
private String login;
#Value("${sms.passwd}")
private String passwd;
}
and this other class
Service("smsService")
public class AltiriaSMSRestServiceImpl implements SmsService {
private final SmsConfig smsConfig;
public AltiriaSMSRestServiceImpl(SmsConfig smsConfig) {
this.smsConfig = smsConfig;
}
#Override
public boolean sendSMS(String msg, String to) throws Exception {
...
}
...
}
That I want to test using this file:
#ContextConfiguration(classes = { SmsConfig.class })
#RunWith(MockitoJUnitRunner.class)
public class AltiriaSMSRestServiceImplTest {
#InjectMocks
private AltiriaSMSRestServiceImpl smsService;
#Test
public void testSendSMS() throws Exception {
smsService.sendSMS("this is a test", "+34653776498");
}
}
but I have a nullpointerException because smsConfig is null when running the test

You should use spring runner to run the test case so that spring beans can be injected.
#RunWith(SpringJUnit4ClassRunner.class)

Related

Why is a bean created twice in test when using #PostConstruct?

I have a configuration class that uses a properties file and it works properly.
Now I want to test that code and I have to recognize that the method annotated with #PostConstruct is run twice during the test. (In debug mode I can see that the for-loop is conducted twice.)
The configuration class:
#Slf4j
#RequiredArgsConstructor
#Configuration
#ConfigurationPropertiesScan("com.foo.bar")
public class MyConfig {
private final MyProperties myProperties;
#Autowired
private GenericApplicationContext applicationContext;
#PostConstruct
void init() {
Objects.requireNonNull(myProperties, "myProperties may not be null");
for (final MyProperties.MyNestedProperty nested : myProperties.getApps()) {
log.info("xxx {} created.", nested.getName());
applicationContext.registerBean(nested.getName(), MyContributor.class, nested);
}
}
}
The used properties class:
#Slf4j
#Data
#Validated
#ConfigurationProperties(prefix = MyProperties.CONFIG_PREFIX)
public class MyProperties {
public static final String CONFIG_PREFIX = "xxx";
#Valid
#NestedConfigurationProperty
private List<MyNestedProperty> apps;
#Data
public static class MyNestedProperty {
#NotNull
#NotEmpty
private String abc;
private String xyzzy;
#NotNull
#NotEmpty
private String name;
}
}
My attempt with the test class:
#ExtendWith(SpringExtension.class)
#RequiredArgsConstructor
#ContextConfiguration(classes = MyConfigTest.MyTestConfiguration.class)
class MyConfigTest {
#MockBean
MyProperties myProperties;
ApplicationContextRunner context;
#BeforeEach
void init() {
context = new ApplicationContextRunner()
.withBean(MyProperties.class)
.withUserConfiguration(MyConfig.class)
;
}
#Test
void should_check_presence_of_myConfig() {
context.run(it -> {
assertThat(it).hasSingleBean(MyConfig.class);
});
}
// #Configuration
#SpringBootConfiguration
// #TestConfiguration
static class MyTestConfiguration {
#Bean
MyProperties myProperties() {
MyProperties myProperties = new MyProperties();
MyProperties.MyNestedProperty nested = new MyProperties.MyNestedProperty();
nested.setName("xxx");
nested.setAbc("abc");
nested.setXyz("xyz");
myProperties.setApps(List.of(nested));
return myProperties;
}
}
}
Why does this happen and how can I prevent this behaviour?

How to Capture ApplicatonEvent in Spring boot integration test?

The issue is that Application Event is not being captured in Spring boot test While it works fine for files listening to event in app project.
I want to capture an ApplicationEvent in Spring boot test(don't want to do Unit testing). My goal is to capture this application event and then perform few tasks in my test to verify the end-to-end functionality. Since, the event is not being captured in test case so I am not able to write integration tests.
Please let me know what is wrong with the code.
Thanks All.
package com.example.demo;
import org.springframework.context.ApplicationEvent;
public class CacheRefreshEvent extends ApplicationEvent {
private String message;
private static final long serialVersionUID = 1L;
public CacheRefreshEvent(Object source, String message) {
super(source);
this.message = message;
}
public String getMessage() {
return message;
}
}
package com.example.demo;
import org.springframework.context.ApplicationEvent;
public class CacheRefreshCompleteEvent extends ApplicationEvent {
private String message;
private static final long serialVersionUID = 1L;
public CacheRefreshCompleteEvent(Object source, String message) {
super(source);
this.message = message;
}
public String getMessage() {
return message;
}
}
package com.example.demo;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
#Component
public class CaptureCacheRefreshCompleteEvent implements ApplicationListener<CacheRefreshCompleteEvent> {
private ApplicationEventPublisher applicationEventPublisher;
void applicationEvent() throws InterruptedException {
applicationEventPublisher.publishEvent(new CacheRefreshEvent(this, "event triggered from SolrUtilitiesTest()"));
Thread.sleep(5000);
System.out.println("Finished execution of test.");
}
public void onApplicationEvent(CacheRefreshCompleteEvent cs) {
System.out.println("gotcha in CaptureCachedRefreshCompleteEvent");
}
public void setApplicationEventPublisher(ApplicationEventPublisher arg0) {
this.applicationEventPublisher = arg0;
}
}
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
package com.example.demo;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.ApplicationListener;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
#RunWith(SpringRunner.class)
#DirtiesContext
#SpringBootTest
class DemoApplicationTests implements ApplicationEventPublisherAware, ApplicationListener<CacheRefreshCompleteEvent> {
#Autowired
private ApplicationEventPublisher applicationEventPublisher;
#Test
void applicationEvent() throws InterruptedException {
applicationEventPublisher.publishEvent(new CacheRefreshEvent(this, "event triggered from Springboot test"));
for(int i=0; i< 20; i ++) {
Thread.sleep(1000);
}
System.out.println("Finished execution of test.");
}
public void onApplicationEvent(CacheRefreshCompleteEvent cs) {
System.out.println("gotcha");
}
#Override
public void setApplicationEventPublisher(ApplicationEventPublisher arg0) {
this.applicationEventPublisher = arg0;
}
}
One way could be to create a very simple listener with #TestComponent inside your test and autowire it as a #MockBean.
Proof of concept (tested with Spring Boot 2.2 and 2.1):
#SpringBootTest
public class PublishTest {
#Autowired
private ApplicationEventPublisher applicationEventPublisher;
#MockBean
private Consumer consumer;
#Test
public void test() {
applicationEventPublisher.publishEvent(new TestEvent(this));
// events are synchronous by default
verify(consumer).consumeEvent(any(TestEvent.class));
}
#TestComponent
private static class Consumer {
#EventListener
public void consumeEvent(TestEvent testEvent) {
}
}
private static class TestEvent extends ApplicationEvent {
public TestEvent(Object source) {
super(source);
}
}
}

Spring boot configuration problem with YAML files

I have the following yaml file
proj:
sms:
gateway:
username: d
password: erer
signature: e
url: http://link.somelink.com
actionKeyMap:
OTP_GENERATOR: Value1
CREATE_USER: Value2
I'm trying to bind the actionKeyMap/gateway property into a Java map and it doesn't works.
I've tried the following code
#Component
#ConfigurationProperties(prefix="proj.sms")
public class MessageResolver {
//#Value("${proj.sms.actionKeyMap}")
private Map<String, String> actionKeyMap;
ConfigurationProperties or Value annotation doesn't works.
Adding code as discussed in comments
Application.yml
proj:
sms:
gateway:
username: d
password: erer
signature: e
url: http://link.somelink.com
actionKeyMap:
OTP_GENERATOR: Value1
CREATE_USER: Value2
Spring boot Application.java
package hello;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
//#EnableConfigurationProperties
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
MessageResolver.java
package hello;
import java.util.Map;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
#Component
#ConfigurationProperties(prefix="proj.sms")
public class MessageResolver {
//#Value("${proj.sms.actionKeyMap}")
private Map<String, String> actionKeyMap;
#Value("${proj.sms.actionKeyMap.OTP_GENERATOR}")
private String test;
public String getTest() {
return test;
}
public void setTest(String test) {
this.test = test;
}
public Map<String, String> getActionKeyMap() {
return actionKeyMap;
}
public void setActionKeyMap(Map<String, String> actionKeyMap) {
this.actionKeyMap = actionKeyMap;
}
}
GreetingController.java
package hello;
import java.util.concurrent.atomic.AtomicLong;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class GreetingController {
private static final String template = "Hello, %s!";
private final AtomicLong counter = new AtomicLong();
#Autowired
MessageResolver messageResolver;
#RequestMapping("/greeting")
public Greeting greeting(#RequestParam(value="name", defaultValue="World") String name) {
System.out.println(messageResolver.getTest());
System.out.println(messageResolver.getActionKeyMap());
return new Greeting(counter.incrementAndGet(),
String.format(template, name));
}
}
Greeting.java (in case you try to build complete project)
package hello;
public class Greeting {
private final long id;
private final String content;
public Greeting(long id, String content) {
this.id = id;
this.content = content;
}
public long getId() {
return id;
}
public String getContent() {
return content;
}
}
URL http://localhost:8080/greeting
Console Output from GreetingController (sop on line 22 & 23)
Value1
{OTP_GENERATOR=Value1, CREATE_USER=Value2}
You can get information with this link on how to solve your problem: How to inject a Map using the #Value Spring Annotation?
it will look something like:
actionKeyMap: '{
OTP_GENERATOR: "value1",
CREATE_USER: "value2"
}'
Try :(colon) intead of .(dot)
#Component
#ConfigurationProperties(prefix="proj:sms")
public class MessageResolver {
#Value("${proj:sms:actionKeyMap}")
private Map<String, String> actionKeyMap;
You just need to declare Gateway and ActionKeyMap class to match the ymlproperty. Or you can read the Spring Boot reference here, link.
#Component
#ConfigurationProperties(prefix="proj.sms")
public class MessageResolver {
private Gateway gateway;
private ActionKeyMap actionKeyMap;
//getter/setter
}
``
public class Gateway {
private String username;
private String password;
private String signature;
private String url;
//getter/setter
}
``
public class ActionKeyMap {
private String OTP_GENERATOR;
private String CREATE_USER;
//getter/setter
}

java.lang.IllegalArgumentException: Not a managed type: class while initiating repository bean

Hi I am trying to load some database values at start time of spring boot application. I have autowired service, and in service i have autowired Dao. Below is the error.
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'validationExpressionService': Unsatisfied dependency expressed through field 'validationExpressionDao'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'IValidationExpressionDao': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Not a managed type: class com.ril.nfg.dao.bean.ValidationExpression
I have added #EnitityScan #EnableJPARepository
FYI, Primary key in the case in String, hope that is ok.
Entity
import java.io.Serializable;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
/**
* The Class ValidationExpression.
*/
package com.ril.nfg.dao.bean;
#Entity
#Table(name = "VALIDATION_EXPRESSION")
public class ValidationExpression implements Serializable {
private static final long serialVersionUID = 9096950800262493651L;
private String validationId;
private String expression;
private String createdBy;
private Date createdOn;
private String description;
private String responseCode;
#Id
#Column(name = "VALIDATION_ID", nullable = false, length = 100)
public String getValidationId() {
return validationId;
}
public void setValidationId(String validationId) {
this.validationId = validationId;
}
#Column(name = "EXPRESSION", nullable = false, length = 200)
public String getExpression() {
return expression;
}
public void setExpression(String expression) {
this.expression = expression;
}
//remaining getters and setters
}
Repository
package com.ril.nfg.dao.repos;
import com.ril.nfg.dao.bean.ValidationExpression;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
/**
* The Interface IValidationExpressionDao.
*/
#Repository
public interface IValidationExpressionDao extends JpaRepository<ValidationExpression, String> {
}
Service
import java.util.List;
#Service
public class ValidationExpressionService {
#Autowired
IValidationExpressionDao validationExpressionDao;
public List<ValidationExpression> getAll() {
return validationExpressionDao.findAll();
}
}
Class with #Autwired Service
public class CacheModuleParam implements ApplicationContextAware{
private static List<ValidationExpression> validationExpressionList = null;
#Autowired
ValidationExpressionService validationExpressionService;
#Override
public void setApplicationContext(final ApplicationContext appContext) throws BeansException {
validationExpressionList = validationExpressionService.getAll();
}
}
Application Class
#ComponentScan(basePackages = {"com.ril.nfg"})
#EnableWebMvc
#EnableAutoConfiguration
#SpringBootApplication//(exclude={DataSourceAutoConfiguration.class})
#EnableJpaRepositories(basePackages="com.ril.nfg.dao.repos",entityManagerFactoryRef="oracleEntityManagerFactory")
//#EntityScan(basePackages = "com.ril.nfg.dao.bean")
public class NFGApplication {
public static void main(String[] args) {
SpringApplication.run(NFGApplication.class, args);
}
}
All solutions on internet focuses on #EntityScan. Please help me understand what is wrong with this code. Thanks in advance
Why do you have all this configuration? Simply put our application in the package tree one level above all the other classes and you can go with a class like this:
#SpringBootApplication
public class NFGApplication {
public static void main(String[] args) {
SpringApplication.run(NFGApplication.class, args);
}
}
Packages:
com.ril.nfg <- here you put NFGApplication
And all other classes in subpackages of com.ril.nfg
And then everything will work!

How to create a mocked (by jmockit) spring bean?

I am new to jmockit and would like to mock a bean inside my Java based Spring Application Configuration. I thought (better hoped) it would go like this:
#Configuration
public class MyApplicationConfig {
#Bean // this bean should be a mock
SomeService getSomeService() {
return new MockUp<SomeService>() {#Mock String someMethod() { return ""; }}.getMockInstance();
}
#Bean // some other bean that depends on the mocked service bean
MyApplication getMyApplication(SomeService someService) {
....
}
}
But unfortunatly this fails with "Invalid place to apply a mock-up".
I wonder if I can generate jmockit mocks inside Spring Configuration classes at all. I need the bean because it is referenced by other beans and the whole Spring Context initialization fails if I do not provide the mock as a Spring bean.
Thanks for any help.
Just use your regular Spring configuration. In a test class, declare the type to be mocked with #Capturing. It will mock whatever the implementation class that Spring used.
Edit: added full example code below.
import javax.inject.*;
public final class MyApplication {
private final String name;
#Inject private SomeService someService;
public MyApplication(String name) { this.name = name; }
public String doSomething() {
String something = someService.doSomething();
return name + ' ' + something;
}
}
public final class SomeService {
public String getName() { return null; }
public String doSomething() { throw new RuntimeException(); }
}
import org.springframework.context.annotation.*;
#Configuration
public class MyRealApplicationConfig {
#Bean
SomeService getSomeService() { return new SomeService(); }
#Bean
MyApplication getMyApplication(SomeService someService) {
String someName = someService.getName();
return new MyApplication(someName);
}
}
import javax.inject.*;
import org.junit.*;
import org.junit.runner.*;
import static org.junit.Assert.*;
import mockit.*;
import org.springframework.test.context.*;
import org.springframework.test.context.junit4.*;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = MyRealApplicationConfig.class)
public final class MyApplicationSpringTest {
#Inject MyApplication myApplication;
#Mocked SomeService mockService;
#BeforeClass // runs before Spring configuration
public static void setUpMocksForSpringConfiguration() {
new MockUp<SomeService>() {
#Mock String getName() { return "one"; }
};
}
#Test
public void doSomethingUsingMockedService() {
new Expectations() {{ mockService.doSomething(); result = "two"; }};
String result = myApplication.doSomething();
assertEquals("one two", result);
}
}
import org.junit.*;
import static org.junit.Assert.*;
import mockit.*;
// A simpler version of the test; no Spring.
public final class MyApplicationTest {
#Tested MyApplication myApplication;
#Injectable String name = "one";
#Injectable SomeService mockService;
#Test
public void doSomethingUsingMockedService() {
new Expectations() {{ mockService.doSomething(); result = "two"; }};
String result = myApplication.doSomething();
assertEquals("one two", result);
}
}
Spring-ReInject is designed to replace beans with mocks.

Resources