Why NoSuchBeanDefinedException while running Spring Integration test - spring

I have a Spring Boot project for which one of the developers has written Integration tests against an in-memory DB. Here is some background before I put my question :
We have following Maven modules :
1) web-persistence : which store all entities and interfaces and corresponding implementations. All these classes are in package com.mycompany.persistence.
2) web-services : which stores all our rest controllers and spring security related classes. These are all in package com.mycompany.services
3) web-services module has maven module dependency on web-persistence module.
4) In web-services module we have defined a service UserContextServiceImpl which is implementation of interface IUserContextService. This service basically encapsulates Spring's SecurityContextHolder functionality by exposing only those parts of a current user related information which is needed through its methods so that everywhere we don't have to use SecurityContextHolder. The implementation class is annotated with #Service("userContextService").
5) Interface IUserContextService is in persistence module. There is another class AppDependecyHeper in persistence module which implements ApplicationContextAware and returns UserContextService's concrete instance by calling appContext.getBean('userContextService') where appContext is holding ApplicationContext when application is initialized.
6) Now we don't want to expose Spring Security classes in persistence module(for reasons which are out of context here) and hence above arrangement of getting current user information through a service by calling getBean() method of applicationContext in persistence layer class AppDependencyHelper. This information is then used to update audit fields createdBy and modifiedBy using an EntityListener.
7) Actual application uses only single ApplicationContext where both persistence layer classes and web controllers are loaded in one single spring context.
8) Everything works fine when application runs. However when our integration test Runs the call appContext.getBean('userContextService') fails and throws NosuchBeanDefinitionException as it is unable to find the bean with name 'userContextService'
9) Now finally the code of our integration Test (only giving relevant details) which is located in com.mycompany.persistence.embeddeddb package:
#Runwith(SpringJUnit4ClassRunner.class)
#ConfigurationContext
class MyEntityTest{
#Configuration
#ComponentScan("com.mycompany.persistence")
public static class Config{
.....
}
.....//code which eventually gives call to
AppDependencyHelper's method which in turn tries to retrieve userContextService Bean.
}
Now the questions :
1) Why it fails to retrieve the bean? I have tried addingcom.mycompany.services like below
#ComponentScan({"com.mycompany.persistence","com.mycompany.services"})
but to no avail.
2) What I am able to understand by reading whatever I got is that #ContextConfiguration needs to be provided with either XML files or Annotated classes or WebApplicationInitializers(in my case as we are having only single ApplicationContext) to create an ApplicationContext from #Configuration classes. However I cannot do that as maven starts complaining about circular dependency which is rightly so as my #Service annotated class UserContextServiceImpl is in web-services module which is dependent on web-persistence module already where the test case class is written.
3) The only solution I can think of is moving all integrations test classes to web-services module so that I can tell #ConfigurationContext all the classes from which it should create an ApplicationContext.Am I right?
Last point : The ApplicationContext class is of type GenericApplciationContext when I run tests. When I run application its obviously AnnotatedEmbeddedWebApplicationContext
please let me know if there is any solution to this problem?

From what I'm guessing you are trying to use a component from your web-service module in your integration tests. If that's the case, than yes, you should move your integration test cases to your web-service module - as you've already suggested in your question.
EDIT: You should really rethink your design, because you have a circular dependency problem here: Your web-service module depends on web-persistence, but the implementation of your IUserContextService in web-persistence is found in web-service.
You should probably move your IUserContextService (and all your integration tests) to your web-service package.

Related

#SpringBootTest loads unrequired Bean when making IT

I'm making some Integration Tests for my app and I'm encountering this problem I can't see how to solve.
I'm using Spring Boot 2.4.13 + Spring Data Neo4J 6.1.9
FYI, I deleted the Application default test that comes bundled when you create a project through Spring Initializr, and under /src/test/resources I have a .yml file named application.yml
My IT class looks like this:
#SpringBootTest
public class ClientIT {
#Autowired
private ClientServiceImpl service;
#Autowired
private ClientRepository repository;
#Test
void someTest() {
//Given
//When
//Then
}
}
But when I run this test I get the following Exception:
java.lang.IllegalStateException: Failed to load ApplicationContext
And this is the cause:
Caused by: java.lang.IllegalStateException: The provided database selection provider differs from the ReactiveNeo4jClient's one.
The thing is I don't use SDN's Reactive features at all in my project. I don't even understand why Spring tries to load it. I've created an Issue under the Spring Data Neo4j GitHub repository (https://github.com/spring-projects/spring-data-neo4j/issues/2488) but they could only tell me that ReactiveNeo4jDataAutoConfiguration gets automatically included if there's a Driver or Flux class in the classpath which I don't have.
I've been debugging the Spring internals while booting up the Application after JUnit Jupiter methods to no success.
What I could see is that at some point after JUnit Jupiter tests preparation/initialization, "reactiveNeo4jTemplate" gets injected into DefaultListableBeanFactory's beanDefinitionNames variable.
I've tried many combinations of different annotations intended to be used when making Integration Tests but the one time it worked was after I explicitly excluded ReactiveNeo4jDataAutoConfiguration class through
#EnableAutoConfiguration(exclude=ReactiveNeo4jDataAutoConfiguration.class)
What I've always seen in some blogposts is that by using #SpringBootTest I shouldn't worry about this kind of problem but it looks like I need to add that annotation every time I want to make a new IT test.
My Integration Tests basically consist of bootstrapping the application + web server (tomcat) along with an embedded Neo4J instance and after that, making requests to check everything works as it should. Do I really need to worry about all of this just to make these simple tests?
Thank you
References:
How do I set up a Spring Data Neo4j integration test with JUnit 5 (in Kotlin)?
SprintBootTest - create only necessary beans
Answering my own question after finding what is causing this error:
In the linked Github Issue, one of the developers says having Flux.class in the classpath forces SDN to instantiate Neo4jReactiveDataAutoConfiguration which is what is causing the other reactive beans to instantiate.
Apparently, neo4j-harness brings io.projectreactor (where Flux.class belongs) as an indirect dependency through neo4j-fabric which is the root of our problems.
The Spring Data Neo4j will be fixing this issue in a patch later this week.

Spring Context Test With Just One Bean

What's the recommended way to run a spring boot test where only the one subject under test is configured in the context.
If I annotate the test with
#RunWith(SpringRunner.class)
#SpringBootTest(properties = "spring.profiles.active=test")
#ContextConfiguration(classes = MyTestBean.class)
Then it seems to work - the test passes, the context starts quickly and seems to only contain the bean that I want. However, this seems like an incorrect use of the #ContextConfiguration(classes = MyTestBean.class) annotation. If I understand correctly the class that I reference is supposed to be a Configuration class, not a regular spring service bean or component for example.
Is that right? Or is this indeed a valid way to achieve this goal? I know there are more complex examples like org.springframework.boot.test.autoconfigure.json.JsonTest which use #TypeExcludeFilters(JsonExcludeFilter.class) to control the context - but this seems overkill for my use case. I just want a context with my one bean.
Clarification
I know that I can just construct the one bean I am testing as a POJO without a spring context test and remove the three annotations above. But in my precise use case I am actually reliant on some of the configuration applied to the context by settings in the application-test.properties file - which is why I've made this a Spring Boot test with a profile set. From my perspective this isn't a plain unit test of a single class in isolation of the spring context configuration - the test is reliant on certain configuration being applied (which is currently provided by the spring boot app properties). I can indeed just test the components as a POJO by creating a new instance outside of a spring context, I'm using constructor injection making the providing of necessary dependencies simple but the test does rely on things like the log level (the test actually makes assertions on certain logs being produced) which requires that the log level is set correctly (which is currently being done via logging.level.com.example=DEBUG in a properties file which sets up the spring context).
For starters, reading the documentation first (e.g., the JavaDoc linked below in this answer) is a recommend best practice since it already answers your question.
If I understand correctly the class that I reference is supposed to be
a Configuration class, not a regular spring service bean or
component for example.
Is that right?
No, that's not completely correct.
Classes provided to #ContextConfiguration are typically #Configuration classes, but that is not required.
Here is an excerpt from the JavaDoc for #ContextConfiguration:
Annotated Classes
The term annotated class can refer to any of the following.
A class annotated with #Configuration
A component (i.e., a class annotated with #Component, #Service, #Repository, etc.)
A JSR-330 compliant class that is annotated with javax.inject annotations
Any other class that contains #Bean-methods
Thus you can pass any "annotated class" to #ContextConfiguration.
Or is this indeed a valid way to achieve this goal?
It is in fact a valid way to achieve that goal; however, it is also a bit unusual to load an ApplicationContext that contains a single user bean.
Regards,
Sam (author of the Spring TestContext Framework)
It is definitely a reasonable and normal thing to only test a single class in a unit test.
There is no problem including just one single bean in your test context. Really, a #Configuration is (typically) just a collection of beans. You could hypothetically create a #Configuration class just with MyTestBean, but that would really be unnecessary, as you can accomplish doing the same thing listing your contextual beans with #ContextConfiguration#classes.
However, I do want to point out that for only testing a single bean in a true unit test, best practice ideally leans towards setting up the bean via the constructor and testing the class that way. This is a key reason why the Spring guys recommend using constructor vs. property injection. See the section entitled Constructor-based or setter-based DI of this article, Oliver Gierke's comment (i.e. head of Spring Data project), and google for more information. This is probably the reason you're getting a weird feeling about setting up the context for the one bean!
You can also use ApplicationContextRunner to create your context using a test configuration of your choice (even with one bean if you like, but as other people have already mentioned for one bean it's more reasonable to use the constructor the classical way without using any spring magic).
What I like this way of testing is the fact that test run very fast since you don't load all the context. This method is best used when the tested bean doesn't have any Autowired dependencies otherwise it's more convenient to use #SpringBootTest.
Below is an example that illustrates the way you can use it to achieve your goal:
class MyTest {
#Test
void test_configuration_should_contains_my_bean() {
new ApplicationContextRunner()
.withUserConfiguration(TestConfiguration.class)
.run(context -> {
assertThat(context.getBean(MyTestBean.class)).isNotNull();
});
}
#Configuraiton
public static class TestConfiguration {
#Bean
public MyTestBean myTestBean(){
new MyTestBean();
}
}
}

Spring AOP aspect doesn't get applied if included from an external jar with different package name

I have a spring boot rest service that included an external project in pom as it's dependency. That external project is basically a jar that has spring AOP code.
The base package in my main application that includes this external jar with spring AOP code is x.y.z
The class in external jar where the #before advice is, is under the package a.b.c
With this class under a.b.c package, it doesn't get recognized by the main application where I want to use the spring aop implementation and apply the aspect. However, when I change it's package from a.b.c to x.y.z (which I really can't do in real life) it works fine.
I know that in spring boot service which happens to be the including service, it scans everything under root package given in the application class, x.y.z in this case and that is why aspect works fine if it's class is under x.y.z.
however, the problem is that this spring app jar will be used across multiple applications. So changing package name like this is not an option.
Is there a way to accomplish this without changing the package name of the class where spring app code is ?
Probably component scan is only activated for your application class packages by default. You can extend it to multiple packages, including the aspect package:
XML style configuration:
<context:component-scan base-package="x.y.z, a.b.c" />
Annotation style configuration:
#ComponentScan(basePackages = {"x.y.z", "a.b.c"})
Disclaimer: I am not a Spring user, only an AspectJ expert. I just knew that you can configure component scan, googled the syntax for you and hope it is correct.
Please define the bean (of jar project )inside main application. Give the #ComponentScan(basePackages = {"x.y.z", "a.b.c"}) as well as #EnableAspectJAutoProxy. Also include below piece of code.
ex:
` #Bean
public LoggingHandler loggingHandler()
{
return new LoggingHandler();
}`
Also annotate external jar code with:
`#Aspect
#Component
public class LoggingHandler {`
What #kriegaex suggests is correct. In addition to that, please make sure you are using #Component along with #Aspect. Since #Aspect is not a Spring annotation, Spring won't recognize it and hence your aspect won't be registered. So, using #Component is mandatory to getting aspects to work in Spring environment.

Spring-data-mongo: MongoRepository not being wired unless I add #Component annotation

I am having a little weird behavior with my spring-data-mongo where my repository package is not being scanned by the <mongo:repositories/> tag. I am using spring 3.2.3.RELEASE with spring-data-mongo 1.2.1.RELEASE.
I have a project called edowmis and in it there are 2 maven modules, datalayer and web which a webapp.I am using the datalayer in isolation so the other module can be ignored. I have an application context for datalayer
So I wanted to test my setup by writing a small Unit/Integration test but I've noticed I can't autowire my UserRepository because It says there isn't such a bean
Since I am using IntelliJ I can see certain visuals when things are ok and not ok. I've addec <context:component-scan/> to my application context but no result.
But when I add the #Component annotation it has started identifying the Class.
all information you might need is on pastie.org
Is the #component or #Repository really necessary or something is wrong with my configuration?
Yes, the #Component or #Repository is necessary. The scan simply indicates that spring should look for classes identified via annotations (#Component, #Repository, #Service) and load them as beans. If you don't use repository or component scan, you would have to manually instantiate all spring-managed beans via XML configuration or Java configuration.
You have to tell spring which classes to turn into beans as it doesn't assume everything in the classpath is supposed to be a spring-managed bean, which is why you need to use the annotations.

Autowired spring bean is not a proxy

I'm working on a very small application connecting to a MySQL database.
I'm trying to create table record but getting 'no transaction in progress'.
I have all the right stuff in place:
a service interface MyService and its implementation MyServiceImpl
I have annotated the service impl with #Service
In the controller I used the interface name for the field #Autowired MyService
I have the correct transaction configuration as it was originally generated by roo
There is a public method MyService.create(...) which MyServiceImpl implements
But,
When I remote debug and inspect the controller's myService field what I see is something like
com.some.package.services.MyService#12345 (and NOT something like $Proxy73) which to me is not right, because what should be autowired is the proxy not he target bean (which is what I think this is). If I'm correct then it makes sense that there is no transaction as the annotation would only kick in when invoking a public method annotated with #Transactional on a proxy.
Please tell me why is spring injecting the target bean in this setup.
Thanks
If you have AspectJ-enabled transaction management (<tx:annotation-driven mode="aspectj" .../>) application of transactions happens in-place in the same class, either during build (compile-time weaving) or on startup (load-time weaving).
No new classes are created (like when using cglib) and no proxies (like with ordinary interface-based AOP in Spring). Instead bytecode of MyServiceImpl was modified directly without you even noticing. Unfortunately the only way to see AOP is to decompile your classes. If you use javap -c MyServiceImpl you'll find plenty of references to Spring transaction layer.
If you are using Spring MVC, make sure to scan specific controller classes alone in servlet context file. Otherwise it will scan 2 times and transaction is not available on the application context.

Resources