JUnit not Initializing Services Parameters - spring-boot

I have a SpringBoot application, basically with a structure similar to the following:
application:
#SpringBootApplication
public class MyApplication {
#Autowired
MainService mainService;
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
#KafkaListener(topics = "myTopic")
public void listen(String message) {
this.graphicService.performWork();
}
}
first service:
#Service
public MainService {
#Autowired MyService myService;
public performWork() {
this.myService.doStuff();
}
}
second service:
#Service
public class MyService {
// server.param1 and server.param2 are defined in application.properties file
#Value("${server.param1}")
private String param1;
#Value("${server.param2}")
private String param2;
#PostConstruct
public void initService(){
}
public void doStuff() {
// do stuff assuming the parameters param1 and param 2 of this autowired service have already been initialized
}
}
I have a junit like the following:
#SpringBootTest(classes = MyApplication.class)
class MyServiceTest {
#Test
void testMyService() {
MyService myService = new MyService();
myService.doStuff();
}
}
When I execute testMyService, I get an exception thrown, essentially like this:
java.lang.IllegalStateException: Failed to load ApplicationContext
.
.
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'myApplication': Unsatisfied dependency expressed through field 'mainService';
.
.
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'mainService': Unsatisfied dependency expressed through field 'myService'
.
.
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'myService': Injection of autowired dependencies failed
.
.
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'server.param1' in value "${server.param1}"
The application works fine operationally. I thought that the way I set up the junit, the springboot app would simply fire up and the parameters found in the application.properties file would simply be available to MyService service as are they when I run the application itself (not the junit).
Obviously I am doing something wrong, and the application context is not available the way I have this junit set up. I would be grateful for any ideas for getting this to work properly.
Thanks!

Wire your class under test in the Junit test like in any production code class.
The #SpringBootTest will autodetect the #SpringBootApplication, so no extra parameter is needed. Just wire the needed dependencies like you would in the application classes.
The test will use the src/test/resources/application.properties (or yml) file, if present. If not present, the src/main/resources/application.properties is used. So if you use environment variables in your production application.yml copy this file to the test resources and fill the parameters with dummy parameters for test.
#SpringBootTest
class MyServiceTest {
#Autowired MyService myService;
#Test
void testMyService() {
myService.doStuff();
}
}
If you like you can add the parameters in the test class with #TestPropertySource(properties
#SpringBootTest
#TestPropertySource(properties = {
"server.param1=srv1",
"server.param2=srv2"
})
class MyServiceTest {
...
Make sure you have spring-boot-starter-test in your dependencies.
Maven Example:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>2.7.5</version>
<scope>test</scope>
</dependency>

Related

Spring Boot Unit testing with request scope bean dependency

I'm trying to run Unit tests on a service class that has an auto wired dependency which is request scoped. When running the unit test, it seems like spring container is unable to find the bean to auto wire into the service class. What should I do to fix this?
Application.java
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
AppConfig.java
#Configuration
public class AppConfig {
#Bean
#Scope("request")
public ClassA classA(){
return new ClassA();
}
}
ServiceA.java
#Service
public class ServiceA {
#Autowired
private ClassA classA;
public void doSomething(){
String value = classA.getValue();
System.out.println(value);
}
}
ServiceTest.java
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class ServiceATest {
#Test
void contextLoads() {
}
}
Error message
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'serviceA': Unsatisfied dependency
expressed through field 'classA'; nested exception is
org.springframework.beans.factory.support.ScopeNotActiveException:
Error creating bean with name 'classA': Scope 'request' is not active
for the current thread; consider defining a scoped proxy for this bean
if you intend to refer to it from a singleton; nested exception is
java.lang.IllegalStateException: No thread-bound request found: Are
you referring to request attributes outside of an actual web request,
or processing a request outside of the originally receiving thread? If
you are actually operating within a web request and still receive this
message, your code is probably running outside of DispatcherServlet:
In this case, use RequestContextListener or RequestContextFilter to
expose the current request.

Spring Testing Unsatisfied depencency NoSuchBeanDefinitionException

When I try to run my tests they all fail becaouse they can't find the bean of one of my classes.
Here are my codes which are used in the context:
The exception I get is this:
Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'testProtoAdminController' : Unsatisfied dependency expressed through constructor parameter 2; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'TestProtoCopyService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
TestProtoAdminControllerTest
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = TestProtoAdminController.class)
public class TestProtoAdminControllerTest {
//Some used services
#Before
public void setUp() {
authenticatedMockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
#Test
#WithMockUser
public void testCopyProto() throws Exception {
authenticatedMockMvc.perform(post("/api/admin/{id}/copy", 1)
.contentType(MediaType.APPLICATION_JSON)
.content(asJson(new TestProtoBaseVo()))).andExpect(status().isOk());
}
//Some more tests which are not important in this case
TestProtoCopyService
#Service
public class TestProtoCopyServiceImpl implements TestProtoCopyService {
//Other services and repositories I have to use.
//Methods
}
TestProtoCopyService
public interface TestProtoCopyService {
#Transactional
void copyTestProto(long testProtoId, String sourceTenant, String targetTenant);
}
TestProtoAdminController
#RestController
#RequestMapping("/*")
public class TestProtoAdminController {
private TestProtoCopyService testProtoCopyService;
public TestProtoAdminController(TestProtoCopyService testProtoCopyService {
this.testProtoCopyService = testProtoCopyService;
}
When using #WebMvcTest Spring will prepare everything to test your web layer. This doesn't mean all your beans are scanned and are part of this test application context and ready to inject.
In general, you usually mock the service class of your controller with #MockBean and then use Mockito to specify its behavoir:
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = TestProtoAdminController.class)
public class TestProtoAdminControllerTest {
#MockBean
private TestProtoCopyService mockedService
// the rest
#Before
public void setUp() {
authenticatedMockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
#Test
#WithMockUser
public void testCopyProto() throws Exception {
authenticatedMockMvc.perform(post("/api/admin/{id}/copy", 1)
.contentType(MediaType.APPLICATION_JSON)
.content(asJson(new TestProtoBaseVo()))).andExpect(status().isOk());
}
If you want Spring Boot to bootstrap the whole application context with every bean consider using #SpringBootTest. With this annotation, you can inject any bean to your application. The downside here is that you need to provide the whole infrastructure (database/queues/etc.) for your test.

Intellij Spring: NoSuchBeanDefinitionException: No bean named 'accountDAO' available

This is driving me nuts. I have the following files, it is a very simple setup.
public class MainApp {
public static void main(String[] args) {
//read the spring config java class
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("Config.class");
//System.out.println("Bean names: " + Arrays.toString(context.getBeanNamesForType(AccountDAO.class)));
//get the bean from spring container
AccountDAO accountDAO = context.getBean("accountDAO", AccountDAO.class);
//call the business method
accountDAO.addAccount();
//close the spring context
context.close();
}
}
Config.java:
#Configuration
#ComponentScan("com.aop")
#EnableAspectJAutoProxy
public class Config {
}
LoggingAspectDemo.java:
#Aspect
#Component
public class LoggingAspectDemo {
//this is where we add all our related advices for the logging
//let's start with an #Before advice
#Before("execution(public void addAccount())")
public void beforeAddAccountAdvice() {
System.out.println("\n=======>>>> Executing #Before advice on method addAccount() <<<<========");
}
}
AccountDAO.java
#Component
public class AccountDAO {
public void addAccount() {
System.out.println(getClass() + ": Doing my Db work: Adding an account");
}
}
Everytime I run the MainApp.java, I get:
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'accountDAO' available
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:687)
at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1207)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:284)
All the files are under "com.aop" package so #ComponentScan should be scanning all the components. It looks simple enough but I can't get my hands around the problem, can anyone help me where I am going wrong?
You're invoking the constructor of AnnotationConfigApplicationContext with "Config.class" as String argument, but this constructor is actually for invoking with base packages i.e. the argument must be a package name.
Since you want to use it with the Configuration class, use the constructor which accepts Class instance instead i.e.
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);

Inject #PersistenceContext in test unit for spring

I have problem when injecting entityManager in my test class. I think that this is because i can't load my spring context in my test class.
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'noteDeFraisDAO': Injection of persistence dependencies failed; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'javax.persistence.EntityManagerFactory' available
this is my test class
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {NoteDeFraisTestConfig.class})
#Transactional
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
public class NoteDeFraisDAOIT
{
#Autowired
private NoteDeFraisDAO noteDeFraisDAO;
#Test
public void myTest()
{
}
}
And this is the configuration for the test I know that when I use new there will be no inject of entitiesManager define in the dao but I don't know what should I do.
#Configuration
public class NoteDeFraisTestConfig {
#Bean
NoteDeFraisDAO noteDeFraisDAO()
{
return new NoteDeFraisDAO();
}
}
I have tried to set my ContextConfiguration to my applicationContext but it didn't work I think that it is because the directory WEB-INF doesn't belong to the classpath. How can I fix this??
This is the structure of my project
thanks.
Update
this is my final test class
#WebAppConfiguration
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({"file:web/WEB-INF/applicationContext.xml", "file:web/WEB-INF/db-config.xml","file:web/WEB-INF/dispatcher-servlet.xml","file:web/WEB-INF/security-config.xml"})
#Transactional
public class NoteDeFraisDAOIT
{
#Autowired
private NoteDeFraisDAO noteDeFraisDAO;
#Test
public void myTest()
{
}
}
Yep, the problem is that the ApplicationContext loaded for your test does not contain the LocalContainerEntityManagerFactoryBean. Assuming that's declared properly in applicationContext.xml, the linked solution below will help.
I have tried to set my ContextConfiguration to my applicationContext but it didn't work I think that it is because the directory WEB-INF doesn't belong to the classpath. How can I fix this??
That's covered here: Location of spring-context.xml

spring test fails on mockServletContext unsupportedOperation

I have a set of Integration Tests running for my Spring-Boot 1.3 app. But I had to add the following to get my maximum sessions working:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter implements ServletContextAware {
...
#Override
public void setServletContext(ServletContext servletContext) {
servletContext.getSessionCookieConfig().setHttpOnly(true);
// causes an ApplicationEvent to be published to the Spring ApplicationContext every time a HttpSession commences or terminates
servletContext.addListener(new HttpSessionEventPublisher());
}
...
}
Now when I run my tests, I get the following:
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'webSecurityConfig' defined in file [/Users/davidclark/projects/edmtotal/build/classes/main/com/edelweissco/dental/configuration/WebSecurityConfig.class]: Initialization of bean failed; nested exception is java.lang.UnsupportedOperationException
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553)
...
Caused by: java.lang.UnsupportedOperationException
at org.springframework.mock.web.MockServletContext.addListener(MockServletContext.java:675)
at com.edelweissco.dental.configuration.WebSecurityConfig.setServletContext(WebSecurityConfig.java:123)
...
Here is an example test class (but they all fall with the same exception):
#Transactional
public class ConfigurationSettingsTest extends BaseSpecification {
#Autowired
private ConfigurationSettings configurationSettings;
#Autowired
ConfigurableApplicationContext context
...
}
where BaseSpecification is:
#ContextConfiguration(classes = MyApp, loader = SpringApplicationContextLoader)
#WebAppConfiguration
public class BaseSpecification extends Specification {
#Value('${local.server.port}')
private int serverPort;
def setup() {
RestAssured.port = serverPort;
}
}
It would seem that now when I run my integration tests, a MockServlet is being applied here, and it doesn't support. this feature. When debugging, I see that a SpringBootMockServletContext is trying to be set in setServletContext, and that is where the exception is.
I will post my answer in case anyone else runs into this. The problem was in my BaseSpecification. I added the #WebAppConfiguration and #IntegrationTest to it, and removed #IntegrationTest off the individual integration tests. Apparently this will actually create the ServletContext the way it should be.
#ContextConfiguration(classes = MyApp, loader = SpringApplicationContextLoader)
#WebAppConfiguration
#IntegrationTest
public class BaseSpecification extends Specification {

Resources