Spring Boot Unit testing with request scope bean dependency - spring-boot

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.

Related

JUnit not Initializing Services Parameters

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>

ScopeNotActiveException in spring boot rest api

I have one simple resource class and autowiring bean from BeanConfig class, which declared one bean.
Whenever request is hit on API, then i am getting an exception.
Code:
#Component
public class BeanConfig {
#Autowired
Environment environment;
#Bean
#RequestScope
public Map<String, String> eventAttributes() {
return new HashMap<>();
}
}
public class SampleResource {
#Autowired
SampleServiceImpl sampleService;
#Autowired
private Map<String, String> eventAttributes; }
Following error:
org.springframework.beans.factory.support.ScopeNotActiveException: Error creating bean with name 'scopedTarget.eventAttributes': 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

Optional bean created after dependent service

I'm trying to inject an optional bean into a service. The bean is successfully created but consistently after the dependent service. Any thoughts why this goes wrong?
Spring Boot 2.5.0 is used. Without using the Optional, everything works fine.
Application.java
#SpringBootApplication
#Configuration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
#Bean
public Optional<SomeClass> someClass() {
System.out.println("creating bean");
return Optional.of(new SomeClass("someName"));
}
}
SomeClass.java
#Value
public class SomeClass {
String name;
}
SomeService.java
#Service
public class SomeService {
public SomeService(Optional<SomeClass> some) {
System.out.println(
some.map(SomeClass::getName).orElse("empty")
);
}
}
Output:
empty
creating bean
When autowiring dependencies the Optional should be used at the injection point, to indicate that a bean might or might not be available. The bean cannot be available for different reasons (conditional configuration, or just a setup error leading to null for a bean).
Creating an Optional bean is something different then using Optional for the injection point.
Ideally, you would just create the bean, which would be created eagerly, if processing can fail, handle it and return null. Now when using Optional at the injection point it will see null and Optional.empty() will automatically be injected.

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);

Resources