Spring Boot override interface in test - java

I have a springboot app (v1.3.5)
My main application goes:
#SpringBootApplication
#ComponentScan({"com.akrome"})
public class InboxCoreApplication {
public static void main(String[] args) {
SpringApplication.run(InboxCoreApplication.class, args);
}
}
Somewhere in the packages I have a repo defined as an implementation:
#Repository
public class CouchbaseServiceImpl implements CouchbaseService {
And somewhere else I have a class using that interface:
#Repository
public class InboxObjectRepositoryImpl {
#Autowired
private CouchbaseService couchbaseService;
I also have a test version of the interface:
#Repository
public class CouchbaseServiceTestImpl implements CouchbaseService {
Now. In my test, I want to simply re-point the interface mapping to the test implementation, while keeping everything else as defined by the main config component scan. But it keeps going bananas. The closest I got is:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {InboxCoreApplication.class, TestConfig.class})
public class InboxServiceTest {
#Autowired
CouchbaseServiceTestImpl couchBaseServiceTestImpl;
Where TestConfig is:
#Configuration
public class TestConfig {
CouchbaseServiceTestImpl couchbaseServiceTestImpl = new CouchbaseServiceTestImpl();
public CouchbaseService couchbaseService() {
return couchbaseServiceTestImpl;
}
}
But every time either it complains about duplicated bean (bc it sees both implementations), or it injects 2 different instances of the test implementation, one in the test class and one in the actual program (wat?).
Any suggestion?

i think your problem is with following class. why are you re-implement the CouchbaseService interface and make it as a test version. What is the real point of doing that? you Unit test class should be developed to test the functionality of your main source. you are creating a test version of that source class and create a unit test to test that test version. What is the point of doing that?
#Repository
public class CouchbaseServiceTestImpl implements CouchbaseService {
So the better implementation should be, remove the CouchbaseServiceTestImpl class. write the unit test to directly Test the CouchbaseServiceImpl. (you can remove the TestConfig class also)
so the code should be like this.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {InboxCoreApplication.class})
public class InboxServiceTest {
#Autowired
CouchbaseServiceImpl couchbaseServiceImpl ;
The real reason behind the complaining about the duplicated bean can be described as follows.
If you annotated any class with #Repository, #Component or #Service, the spring IOC container will automatically instantiate those classes and register then in the application context when the Spring application context is loaded.
So you have two implementation of the CouchbaseService interface.
1. CouchbaseServiceImpl
2. CouchbaseServiceTestImpl
since both of these classes have been annotated with #Repository, the spring application container will instantiate both of these classes and will maintain both of those objects in the IOC container. both these instance can be assigned to CouchbaseService interface reference.
look at the following line in your code.
#Autowired
private CouchbaseService couchbaseService;
Now the spring application container is in a doubtful situation to find out the relevant matching bean( whether it should use CouchbaseServiceImpl or CouchbaseServiceTestImpl. both of the beans are eligible for the assignment). Therefore it warns about the duplicate and does not do the assignment.
You have two options.
option 01.
remove the #Repository annotation in one of those classes. preferably remov it in CouchbaseServiceTestImpl. (better to remove that class as i have explained earlier)
option 02
keep all the classes as you wanted. user #Qualifier to tell the spring application container about the real matching bean for the assignment.
so the code should be changed as follows.
#Repository("couchbaseServiceTestImpl")
public class CouchbaseServiceTestImpl implements CouchbaseService {
#Repository("couchbaseServiceImpl")
public class CouchbaseServiceImpl implements CouchbaseService {
#Repository
public class InboxObjectRepositoryImpl {
#Autowired
#Qualifier("couchbaseServiceTestImpl")
private CouchbaseService couchbaseService;
So it is going to assign an instance of CouchbaseServiceTestImpl to CouchbaseService reference.

I think I found it.
It's supposed to user profiles:
#Repository
#Profile("application")
public class CouchbaseServiceImpl implements CouchbaseService {
and
#Repository
#Profile("test")
public class CouchbaseServiceTestImpl implements CouchbaseService {
and in my test I simply:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {InboxCoreApplication.class})
#ActiveProfiles("test")
public class InboxObjectRepositoryImplTest {

Related

How to create some test spring beans before others

I have production bean A and in #TestConfiguration bean B. Non of those beans depends on each other. But in tests I want bean B to be instantiated before bean A. that's because B prepares local testing environment for A. How can i achieve it?
i can't just annotate A with #DependsOn because B is available only in some tests and i don't want to bind production code with tests.
But in tests I want bean B to be instantiated before bean A
...
and i don't want to bind production code with tests.
Something like this?
#Profile("test")
#Bean
public BeanB beanB() {
return new BeanB();
}
#Profile("test")
#Bean
public BeanA beanA(BeanB beanB) {
return new BeanA();
}
#Profile("!test")
#Bean
public BeanA beanA() {
return new BeanA();
}
There's no easy way to do what you're asking. Your best bet is to have an optional dependency in A. In production, the dependency will be missing and so will be ignored. In the tests, you will provide a bean which contains your test preparation implementation.
Yes, you are adding a little bit of additional complexity for the sake of tests which is not ideal but it's very limited.
Forgive the terrible names:
interface APreparer {
void prepareA();
}
#Component
public class A {
public A(final Optional<APreparer> preparer) {
preparer.ifPresent(APreparer::prepareA);
}
}
You can create a TestApplication (in src/test/java) that trivially extends your actual Spring Boot application class and have that depend on your test bean. Then tell SpringBootTest to load your TestApplication instead of the production application.
Example TestApplication:
#DependsOn("beanB")
#Configuration
#Import(YourApplication.class)
public class TestApplication {}
Example test class:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = TestApplication.class)
public class YourApplicationTests {
...

Spring MVC - Override a bean for one Integration test

I have a project with spring MVC v5.0.8, Java 8
I've made some integration test from controller to database, and now, I want to add one which will test what happens if the first part of a transactional behavior failed. I'll ensure that the transaction is effectively roll-backed.
So, I've to override a DAO to make it fail. After some research, came up a simple idea : override spring config for that test : Overriding an Autowired Bean in Unit Tests
My test work fine now, but the problem is that this configuration is shared with other test from other classes, even if it is in one class. How can I make it specific to that test ?
(If b creation fail, a must be roll-backed)
Failing test :
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#EnableWebMvc
#Sql({"/sqlfiles/clean-data.sql"})
public class AControllerFailDaoIt {
#Configuration
static class ConfigFailDao extends ApplicationConfiguration{
#Bean
#Primary
public BDao getBDao() {
return new BDao() {
//Overriding method to make it fail
};
}
}
#Autowired
AController aController;
#Autowired
ADao aDao;
#Test
public void should_not_create_a_if_b_failed(){
//creation of a
//assert nor a nor b are created
}
}
Nominal test case :
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#EnableWebMvc
#ContextConfiguration(classes = {ApplicationConfiguration.class, CustomWebAppConfiguration.class})
#Sql({"/sqlfiles/clean-data.sql"}) //"/sqlfiles/account-test.sql"
public class AControllerIT {
#Autowired
AController aController;
#Autowired
ADao aDao;
#Autowired
BDao bDao;
#Test
public void should_create_a_and_corresponding_b(){
//create a
//assert that both a and b are created
}
}
ApplicationConfiguration (which is test-specific)
#Configuration
#ComponentScan(basePackages = "my.base.package")
class ApplicationConfiguration implements WebMvcConfigurer {
}
Note : my Integration test classes are within the base package, does it matters ?
I found an option : put the fail test config in an external class, and call it only on my fail test, but it still doesn't work.
At the moment, I ran out of ideas, so I'll welcome your help !
I suggest you to use #Qualifier annotation.
Instead of putting #Primary above getBDao() method in your configuration put #Qualifier with some name i.e.:
#Bean
#Qualifier("BDaoTest")
public BDao getBDao() {
return new BDao() {
//Overriding method to make it fail
};
}
After that put #Primary on your default BDao implementation (in your default config).
Then when you autowire this bean in your test you need to put this qualifier:
#Autowired
#Qualifier("BDaoTest")
BDao bDao;
I got it working. In a bad way, so if you have a better option, I'm in. But I didn't found a way to make my test fail by data... Which would be the better one.
So, I've found that the #Configuration annotation make a class scanned. Spring doc.
I just made my fail configuration in an outer class, and remove the #Configuration annotation. So it won't be scanned by other test configuration. Then in my fail test class, I referenced it in the #ContextConfiguration annotation, so it is used. This way it work fine. I now have a problem with #Transactional, but that's not this thread.
Thanks to responders, you made me look in the right direction :-)

Spring Boot: #TestConfiguration Not Overriding Bean During Integration Test

I have a Bean defined in a class decorated with #Configuration:
#Configuration
public class MyBeanConfig {
#Bean
public String configPath() {
return "../production/environment/path";
}
}
I have a class decorated with #TestConfiguration that should override this Bean:
#TestConfiguration
public class MyTestConfiguration {
#Bean
#Primary
public String configPath() {
return "/test/environment/path";
}
}
The configPath bean is used to set the path to an external file containing a registration code that must be read during startup. It is used in an #Component class:
#Component
public class MyParsingComponent {
private String CONFIG_PATH;
#Autowired
public void setCONFIG_PATH(String configPath) {
this.CONFIG_PATH = configPath;
}
}
While trying to debug this I set a breakpoint inside each method as well as the constructor of the test config class. The #TestConfiguration's constructor breakpoint is hit, so i know that my test configuration class instantiates, however the configPath method of that class is never hit. Instead, the configPath method of the normal #Configuration class is hit and the #Autowired String in MyParsingComponent is always ../production/environment/path rather than the expected /test/environment/path.
Not sure why this is happening. Any thoughts would be greatly appreciated.
As documented in the Detecting Test Configuration section of the Spring Boot reference manual, any beans configured in a top-level class annotated with #TestConfiguration will not be picked up via component scanning. So you have to explicitly register your #TestConfiguration class.
You can do that either via #Import(MyTestConfiguration.class) or #ContextConfiguration(classes = MyTestConfiguration.class) on your test class.
On the other hand, if your class annotated with #TestConfiguration were a static nested class within your test class, it would be registered automatically.
Make sure that the method name of your #Bean factory method does not match any existing bean name. I had issues with method names like config() or (in my case)
prometheusConfig() which collided with existing bean names. Spring skips those factory methods silently and simply does not call them / does not instantiate the beans.
If you want to override a bean definition in your test, use the bean name explicitly as string parameter in your #Bean("beanName") annotation.
Test configuration has to be explicitly imported in the test via #Import({MyTestConfiguration.class}).
The name of the #Bean methods in #Configuration and #TestConfiguration have to be different. At least it makes difference in Spring Boot v2.2.
Also make sure spring.main.allow-bean-definition-overriding=true otherwise the bean could not be overriden.
For me worked this code:
#TestConfiguration // 1. necessary
public class TestMessagesConfig {
#Bean
#Primary // 2. necessary
public MessageSource testMessageSource() { // 3. different method name than in production code e.g. add test prefix
}
}
I struggled with a related problem, whereby even though I was using an inner static class, my test bean was not being registered.
It turns out, You still need to add your inner static class to the #ContextConfiguration class array, otherwise the beans inside the #TestConfiguration doesn't get picked up.
public interface Foo {
String execute();
}
public class FooService {
private final Foo foo;
FooService(Foo foo) {
this.foo = foo;
}
public String execute() {
return foo.execute();
}
}
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = {FooService.class, FooTest.FooTestConfig.class})
public class FooTest {
#Autowired
FooService fooService;
#Test
void test() {
Assertions.assertEquals("MY_TEST_BEAN", fooService.execute());
}
#TestConfiguration
static class FooTestConfig {
#Bean
public Foo getFooBean() {
return () -> "MY_TEST_BEAN";
}
}
}
I came across a similar issue recently and got it sorted out by annotating my testing bean with #Primary as well as #Bean. Not sure why it's required, which seems not documented in the Spring doc. The version of my SpringBoot is 2.0.3.
In my case it was an issue with #RunWith(SpringRunner.class), I'm not exactly sure why it wasn't working, I was following this - Testing in Spring Boot
But after replacing that with #ExtendWith(SpringExtension.class) the inner static #TestConfiguration class created the beans as expected.
Maybe a version mismatch - I'm using Spring Boot 2.7.2.
In my case replacing #Import(TestConfig.class) with #ContextConfiguration(classes=TestConfig.class) did the trick. For some reason, some of the beans from TestConfig but 1 wasn't until I replaced #Import with #ContextConfiguration.
This was also mentioned in some comments that were hidden because they had no upvotes.
I found it odd how several answers stated that the names of the #Beans have to be different from each other. How would that make one override the other?
There wasn't one specific answer that worked for me, but I've solved the issue by combining some of their advices.
Here's what worked for me.
Main configuration class:
#Configuration
public class SpringConfiguration {
#Bean
BeanInterface myBean() {
return new BeanImplementation();
}
#Bean
OtherClass otherBean() {
return new OtherClass();
}
}
Test configuration class:
#TestConfiguration
public class TestSpringConfiguration {
#Bean
#Primary
BeanInterface myBean() {
return new TestBeanImplementation();
}
}
Test class:
#SpringBootTest(classes = TestSpringConfiguration.class,
properties = "spring.main.allow-bean-definition-overriding=true")
public class Tests {
#Test
public void test() {
// do stuff
}
}
In this way, the "myBean" bean instance is the one defined in the TestSpringConfiguration class, while "otherBean" is the one defined in the SpringConfiguration class, since it's not overridden.
If I gave two different names to the "myBean" beans, the "real" one would still be initialized and, in my case, would give an error during tests, since it needs something that's only available at runtime in its proper environment.
Once I gave both the same name, Spring would throw an error saying that they were conflicting. Hence why I had to specify the property spring.main.allow-bean-definition-overriding=true in the #SpringBootTest annotation of the test class.
By the way, if you're NOT using Spring Boot, I guess these alternative annotations could work for you:
#ExtendWith(value = SpringExtension.class)
#ContextConfiguration(loader = AnnotationConfigContextLoader.class, // <- not sure about this one
classes = { SpringConfiguration.class, TestSpringConfiguration.class })
public class Tests {
#Test
public void test() {
// do stuff
}
}
Then, you would still have to set the property spring.main.allow-bean-definition-overriding=true in the test application.yml or application.properties file, or in some other way via code on startup.
Note: I'm not 100% sure that you would need the loader = AnnotationConfigContextLoader.class thing. Try without it, first. I needed it in a project of mine which had Spring without Boot, but I can't remember whether it's a standard thing to set or I needed it for some specific reason.

Best way to exclude unit test #Configurations in Spring?

In my Spring Boot project, I have a main class annotated with #SpringBootConfiguration. I also have some unit tests that use #SpringApplicationConfiguration that points to an inner class that defines a Spring context for usage in my unit test (using some mocks).
I now want to write an integration test that starts the full context of my application. However, this does not work as it also picks up the Spring contexts that are defined as inner classes in other unit tests.
What would be the best way to avoid that? I did see the exclude and excludeName properties on #SpringBootConfiguration, but I am unsure how to use them.
UPDATE:
Some code to explain the problem more:
My main class:
package com.company.myapp;
#SpringBootApplication
#EnableJpaRepositories
#EnableTransactionManagement
#EntityScan(basePackageClasses = {MyApplication.class, Jsr310JpaConverters.class})
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
I have a unit test for Spring REST Docs:
package com.company.myapp.controller
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration
#WebAppConfiguration
public class SomeControllerDocumentation {
#Rule
public final RestDocumentation restDocumentation = new RestDocumentation("target/generated-snippets");
// Some test methods here
// Inner class that defines the context for the unit test
public static class TestConfiguration {
#Bean
public SomeController someController() { return new SomeController(); }
#Bean
public SomeService someService() { return new SomeServiceImpl(); }
#Bean
public SomeRepository someRepository() { return mock(SomeRepository.class);
}
So the unit test uses the context defined in the inner class. Now I want a different test that tests if the "normal" application context of the app starts up:
package com.company.myapp;
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(MyApplication.class)
#WebAppConfiguration
public class MyApplicationTest {
#Autowired
private ApplicationContext applicationContext;
#Test
public void whenApplicationStarts_thenContextIsInitialized() {
assertThat(applicationContext).isNotNull();
}
}
This test will now not only wire the stuff it should, but also the beans from the SomeControllerDocumentation.TestConfiguration inner class. This I want to avoid.
You could use profiles: annotate the relevant configuration beans with #Profile("unit-test") and #Profile("integration-test") and inside the tests specify which profile should be active via #ActiveProfiles.
However, it sounds like you could avoid the problem altogether just by reorganising your configuration structure. It's hard to assume anything without seeing any code, though.
Since Spring Boot 1.4, the problem can be avoided by annotation the configuration in the unit tests with #TestConfiguration
I think you talk about #Ignore

Where to put #Bean in Spring Boot?

I am wondering what the best place would be for a Spring Boot app to register additional beans. I have a Main class that is annotated with #SpringBootApplication and beans defined in that class are picked up. But when i put those beans in another class it seems that the are not being registered.
When reading the documentation i got the idea that the #SpringBootApplication would implicitly search for classes that have #Bean annotations in them.
So my options are now:
Put all #Bean annotated bean in my main class
#SpringBootApplication
public class MyApplication {
#Bean
public Filter AuthenticationFilter() {
return new AuthenticationFilter();
}
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
Create a configuration class and annotate that with #Configuration
#Configuration
public class MyConfiguration {
#Bean
public Filter AuthenticationFilter() {
return new AuthenticationFilter();
}
}
Is there a better way of doing this?
It is pretty much a matter of preference, but it is generally considered best practice to put exposed beans in configuration classes, which are logically grouped.
For example, you might have several configuration classes with a number of beans contained within each: An authentication configuration class with beans for AuthenticationProvider or UserDetailsService; a Thymeleaf configuration class containing beans for various Thymeleaf dialects, etc.
Actually, it is your choice there is no spring standard present to tell which one is best but while defining a class OOP design principles says A class should be loosely coupled and highly cohesive, should follow Single Responsibility Principle (SRP), Here
Coupling --> Degree of knowledge a class has about another class
Cohesion --> Degree which tells how well focused your class is
SRP --> A class should have only one responsibility, there should be only one reason to change a class.
So according to cohesion and SRP principle class should be well focused and have only one responsibility.
Here in your case you have only 2 beans but in future, these beans might increase. So should follow your second point and create another class for your bean declaration.
And In my choice should even create more configuration classes, So one configuration class should have a similar type of beans.
Yes, including your beans inside the #Configuration class is usually the preferred way for Spring.
This is also one of the ways Spring recommends injecting inter-dependencies between beans is shown in the following sample copied from the Spring's reference guide here:
Additionally, the default scope of #Beans is SINGLETON, if you specify a different scope such as PROTOTYPE the call will be passed to the original method.
Have a look at this section in the Spring Reference Guide
It depends on where the main class is located which has generally #SpringBootApplication annotations. You can annotate this main class with #ComponentScan(basePackageClasses = HelloWorld.class). Here HelloWorld has bean definitions annotated with #Beans and the class is annotated with #Configurations.
Spring container will scan all the sub-packages of the class specified in #ComponentScan arguments. You can also give wild card entries instead of class name.
Ex:
#SpringBootApplication
#ComponentScan(basePackageClasses = HelloWorld.class)
public class DemoApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class);
}
}
**Bean Class:**
#Configuration
public class HelloWorld {
#Bean
public TweetUserSevice tweetUserSevice() {
return new TweetUserSeviceImpl();
}
}
It depends on where the main class is located which has generally #SpringBootApplication annotations. You can annotate this main class with #ComponentScan(basePackageClasses = HelloWorld.class). Here HelloWorld has had definitions annotated with #Beans and the class is annotated with #Configurations.
Spring container will scan all the sub-packages of the class specified in #ComponentScan arguments. You can also give wild card entries for basePackageClasses argument instead of class name as specified above. E.g.
#SpringBootApplication
#ComponentScan(basePackageClasses = HelloWorld.class)
public class DemoApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class);
}
}
Bean Class:
#Configuration
public class HelloWorld {
#Bean
public TweetUserSevice tweetUserSevice() {
return new TweetUserSeviceImpl();
}
}
Another approach:
Generally in big projects, we will have multiple spring config classes containing bean definitions. We can avoid worrying about that all the bean class should be in sub-package of main class. What we can do is that we can have a single master spring config class(but make sure this master spring config class is under sub-package of main class so that #SpringBootApplication annotations automatically detects the master config) and import all the other bean classes.
I have 2 bean classes (TweetBeansConfig, TweetSystemHealthBeansConfig) in the package com.ronak.tweet (This package is not sub-package where main class exists). I have one master spring config class (TweetMasterSpringConfig) and this class resides in package which is sub-package where my main class resides.
package com.ronak.tweet.beans;
#Configuration
#Order(value=1)
#Import({
TweetBeansConfig.class,
TweetSystemHealthBeansConfig.class
})
public class TweetMasterSpringConfig {
public TweetMasterSpringConfig() {
System.out.println("Initilaizing master spring config");
}
}
package com.ronak.beans;
#Configuration
public class TweetBeansConfig {
#Bean
{
//
}
}
package com.ronak.beans;
#Configuration
public class TweetSystemHealthBeansConfig {
#Bean
{
//
}
}
Main class
package com.ronak.tweet;
#SpringBootApplication
public class DemoApplication extends SpringBootServletInitializer {
/**This is for registing REST layer for Jersey which implements jaxb. It will register all the classes which is in the pacakage com.ronak.tweet.rest. You can add comma separated package names too.
#Bean
ResourceConfig resourceConfig() {
return new ResourceConfig().packages("com.ronak.tweet.rest");
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class);
}
}
It depends on personal choices and there is no good or bad way to do it.
As it is preferred or showed way in documentation of Spring Boot references.
As annotating main class with #SpringBootApplication makes it convenient to Bootstrap spring app. But it does only looks for subpackages so nesting configuration inside subfolder would not detect the #Bean automatically that is the only thing to remember other than it's all about personal preferences.

Categories

Resources