Autowiring fails in Junit testing and spring #configuration - java

I have two #Configuration classes. I need a bean from one configuration class to another. I have autowired the configuration 1 into 2. All works fine. When executing the unit testing, am getting the below exception.
setUpContext(com.trafigura.titan.framework.services.messaging.loader.SpringLoadTest)
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'com.xxx.MessagingServicesConfig': Injection of autowired dependencies failed;
nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.xxx.EMSJMSConfig com.xxx.MessagingServicesConfig.emsJmsConfig;
nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type[com.xxx.EMSJMSConfig] found for dependency:
expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
Is there anything I need to do additionally to make this working?
Below is the setup for testing.
#Configuration
#Import({MessagingServicesConfig.class,...,EMSJMSConfig.class
})
public class MessagingConfig {}
#Profile("EMS-MESSAGING")
#Configuration
public class EMSJMSConfig {
#Bean
public javax.jms.ConnectionFactory jmsSubscriberConnectionFactory() throws JMSException {
SingleConnectionFactory singleConnectionFactory = new SingleConnectionFactory(tibjmsConnectionFactory());
return singleConnectionFactory;
}
}
#Configuration
public class MessagingServicesConfig {
#Autowired
private EMSJMSConfig emsJmsConfig;
#Bean(destroyMethod = "shutdown")
public MessagingService messagingService() throws JMSException {
...
ConnectionFactory cf=emsJmsConfig.jmsSubscriberConnectionFactory(); // Getting NPE at this line.
}
}
and finally the test class,
public class MessagingServicesConfigTest {
private MessagingServicesConfig config;
private EMSJMSConfig emsJmsConfig;
#BeforeMethod
public void setUp() throws Exception {
config = new MessagingServicesConfig();
... //what needs to be done here to have the EMSJMSConfig
}
#Test
public void testBuildsCorrectService() throws JMSException {
MessagingService service = config.messagingService();
...
}
}

By calling new you're creating object yourself, Spring doesn't know anything about it.
Moreover, you should have a test configuration which will be aware of your beans.
Use an appropriate Runner to load SpringContext.
#ContextConfiguration(classes = TestConfig.class)
#RunWith(SpringRunner.class)
class Tests {
#Autowired // if needed
private MessagingServicesConfig config;
}
While in TestConfig you can create beans or import configuration from the Application:
#Configuration
#Import({MessagingServicesConfig.class})
public class TestConfig {}
#Configuration
#Import({EMSJMSConfig.class})
public class MessagingServicesConfig {}
Or you can refer to your config classes directly:
#ContextConfiguration(classes = {MessagingServicesConfig.class, EMSJMSConfig.class})

Related

How to inject mocks with Springboot?

I want to inject a mock into my Springboot application. I get this error:
Error creating bean with name 'ca.company.TestA': Unsatisfied dependency expressed through field 'a'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'ca.company.hello.A' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
I'm stuck and don't understand how to process. I Defined A to be autowired. What's the problem?
Here is my test file:
#RunWith(SpringJUnit4ClassRunner.class)
public class TestA {
#Autowired
private A a;
private B Bmock;
#Before
public void before() {
Bmock = Mockito.mock(B.class);
ReflectionTestUtils.setField(a, "b", Bmock);
}
#Test
public void testSpeak() {
when(Bmock.writeToScreen()).thenReturn("Everything will be alright");
assert(true);
}
}
here is the config file:
#Configuration
public class Config {
#Bean
public B b() {
return new B();
}
// The error persists whether I define this bean or not
#Bean
public A a() {
return new A();
}
}
And here is the class in question:
#Component
public class A {
#Autowired
private B b;
public void speak() {
System.out.println(b.writeToScreen());
}
}
And finally here is my file structure:
What am I doing wrong, I don't get it.
Your configuration class is not processed by Spring. The simplest way to achieve that is to put #ContextConfiguration(classes = Config.class) in your test class.

Spring: How to make a service conditional on the availability of a PlatformTransactionManager bean?

I have a FooService that I would like to be available only when a PlatformTransactionManager is available.
If I define my service like this and no PlatformTransactionManager is available, then my application will fail to start:
#Service
public class FooService {
public FooService(final PlatformTransactionManager txManager) { ... }
...
}
I wanted to use ConditionalOnBean, which should only annotate auto configuration classes. I refactored my code like this:
#Configuration
public class FooAutoConfiguration {
#Bean
#ConditionalOnBean(PlatformTransactionManager.class)
public FooService fooService(final PlatformTransactionManager txManager) {
return new FooService(txManager);
}
}
public class FooService {
public FooService(final BarBean bar) { ... }
...
}
I wrote the following test for FooService:
#ExtendWith(SpringExtension.class)
#Import(FooAutoConfiguration.class)
public class FooServiceTest {
#Autowired
private FooService fooService;
#Test
public void test() {
System.out.println("fooService = " + fooService);
}
}
But I get the following exception when I try to run it:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.acme.FooServiceTest': Unsatisfied dependency expressed through field 'fooService'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.acme.FooService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:596)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:90)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:374)
(...)
However, I know that a PlatformTransactionManager bean is available, because the test runs fine when I #Autowire a PlatformTransactionManager in my test instead of a FooService.
Interestingly, I also tried to replace PlatformTransactionManager with WebClient.Builder, and everything ends up working as it should. What is so special about PlatformTransactionManager?
How can I write FooService so that it will work when a PlatformTransactionManager bean is available, and not prevent applications where no such bean is available from starting?
Add #AutoConfigureOrder annotation to ensure that your auto-configuration class is processed after the transaction manager bean is registered by Spring Boot:
#Configuration
#AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE)
public class FooAutoConfiguration {
}

Unable to autowire RestTemplate for unit test

I have a service which uses an autowired instance of RestTemplate like below
#Service
class SomeAPIService {
private RestTemplate restTemplate;
SomeAPIService(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
this.restTemplate.setRequestFactory(HttpUtils.getRequestFactory());
}
}
Everything runs fine in non-test environment. But when I try to run following unit test in test profile, it starts complaining about unable to autowire rest template.
#RunWith( SpringJUnit4ClassRunner.class )
#SpringBootTest(classes = MyApplication.class, webEnvironment = RANDOM_PORT, properties = "management.port:0")
#ActiveProfiles(profiles = "test")
#EmbeddedPostgresInstance(flywaySchema = "db/migration")
public abstract class BaseTest {
}
#SpringBootTest(classes = SomeAPIService.class)
public class SomeAPIServiceTest extends BaseTest {
#Autowired
SomeAPIService someAPIService;
#Test
public void querySomeAPI() throws Exception {
String expected = someAPIService.someMethod("someStringParam");
}
}
Following is the detailed exception -
Caused by:
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'someAPIService': Unsatisfied dependency
expressed through constructor parameter 0; nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type 'org.springframework.web.client.RestTemplate'
available: expected at least 1 bean which qualifies as autowire
candidate. Dependency annotations: {}
Any clues?
Following helped me get the correct dependencies autowired. The solution is to also include RestTemplate.class in list of classes given to SpringBootTest.
#SpringBootTest(classes = {RestTemplate.class, SomeAPIService.class})
class SomeAPIService {
#Autowired
SomeAPIService someAPIService;
#Test
public void querySomeAPI() throws Exception {
String expected = someAPIService.someMethod("someStringParam");
}
}
#Emre answer was helpful in guiding me towards the final solution.
You are trying to autowire SomeAPIService without satisfying its dependencies. You should inject Rest Template to SomeAPIService. But you are getting NoSuchBeanDefinitionException for Rest Template.
Take a look how to inject it :
How to autowire RestTemplate using annotations
Alternative answer would be - to use TestRestTemplate
From official docs >>>
TestRestTemplate can be instantiated directly in your integration tests, as shown in the following example:
public class MyTest {
private TestRestTemplate template = new TestRestTemplate();
#Test
public void testRequest() throws Exception {
HttpHeaders headers = this.template.getForEntity(
"https://myhost.example.com/example", String.class).getHeaders();
assertThat(headers.getLocation()).hasHost("other.example.com");
}
}
Alternatively, if you use the #SpringBootTest annotation with WebEnvironment.RANDOM_PORT or WebEnvironment.DEFINED_PORT, you can inject a fully configured TestRestTemplate and start using it. If necessary, additional customizations can be applied through the RestTemplateBuilder bean.

Why spring can't find my bean?

I created an interface and a class:
public interface UserService {
List<User> listAll();
}
#Transactional
public class DefaultUserService implements UserService {
private String tableName;
public List<User> listAll() { someDao.listAllFromTable(tableName); }
public void setTableName(String tableName) { this.tableName = tableName; }
}
Also in my application context xml file context.xml, I defined:
<bean id="userService" class="mypackage.DefaultUserService">
<property name="tableName" value="myusers" />
</bean>
Then I want to test the DefaultUserService:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath:context-test.xml"})
#TransactionConfiguration(transactionManager = "testTransactionManager")
#Transactional
public class UserServiceTest {
#Autowired
private DefaultUserService userService;
#Before
public void setup() {
userService.setTableName("mytesttable");
}
#Test
public void test() {
// test with userService;
userService.listAll();
}
}
Notice it uses context-test.xml, which imported the original context.xml:
<import resource="classpath:context.xml"/>
Unfortunately, when the test starts, spring throws exception:
org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'mypackage.UserServiceTest':
Injection of autowired dependencies failed;
nested exception is org.springframework.beans.factory.BeanCreationException:
Could not autowire field:
private mypackage.DefaultUserService mypackage.userService
nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type [mypackage.DefaultUserService] found for dependency:
expected at least 1 bean which qualifies as autowire candidate for this dependency.
Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
I'm not sure where is wrong, why spring can't find the bean DefaultUserService I defined?
It's because #Transactional places the bean is behind a jdk proxy implementing UserService interface, after that the bean is only available as UserService and not DefaultUserService.
See https://stackoverflow.com/a/18875681/241986.
You can try setting the table name with a property placeholder #Value("${someprop}") and define that property in test context, or create another interface that will expose setTableName(), and autowire that helper interface into the test case.
I'm not sure there are any easy solutions of the problem, I think this task can be subsumed under the problem of bean redefinition in Spring test-context framework
Spring beans redefinition in unit test environment
Try to replace the class DefaultUserService to the interface UserService
public class UserServiceTest {
#Autowired
private UserService userService;
....
}
You have not defined the getter for your property tableName in your implementing class.Spring IOC container works on the POJO model

Unit testing a Spring Boot service class with(out) repository in JUnit

I am working on a spring boot based webservice with following structure:
Controller (REST) --> Services --> Repositories (as suggested in some tutorials).
My Database Connection (JPA/Hibernate/MySQL) is defined in a #Configuration class. (see below)
Now I'd like to write simple tests for methods in my Service classes, but I don't really understand how to load ApplicationContext into my test classes and how to mock the JPA / Repositories.
This is how far I came:
My service class
#Component
public class SessionService {
#Autowired
private SessionRepository sessionRepository;
public void MethodIWantToTest(int i){
};
[...]
}
My test class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(loader = AnnotationConfigContextLoader.class)
public class SessionServiceTest {
#Configuration
static class ContextConfiguration {
#Bean
public SessionService sessionService() {
return new SessionService();
}
}
#Autowired
SessionService sessionService;
#Test
public void testMethod(){
[...]
}
}
But I get following exception:
Caused by: org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'sessionService': Injection of autowired
dependencies failed; nested exception is
org.springframework.beans.factory.BeanCreationException: Could not
autowire field: private com.myApp.SessionRepository
com.myApp.SessionService.sessionRepository; nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type [com.myApp.SessionRepository] found for
dependency: expected at least 1 bean which qualifies as autowire
candidate for this dependency. Dependency annotations:
{#org.springframework.beans.factory.annotation.Autowired(required=true)}
For completeness: here's my #Configuration for jpa:
#Configuration
#EnableJpaRepositories(basePackages={"com.myApp.repositories"})
#EnableTransactionManagement
public class JpaConfig {
#Bean
public ComboPooledDataSource dataSource() throws PropertyVetoException, IOException {
...
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource, JpaVendorAdapter jpaVendorAdapter) {
...
}
#Bean
public JpaVendorAdapter jpaVendorAdapter() {
...
}
#Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
...
}
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation(){
...
}
}
use #SpringBootTest and #RunWith(SpringRunner.class) to load the context
#RunWith(SpringRunner.class)
#SpringBootTest
class Demo{
#Test
void contextLoad(){}
}
In your test Spring will use configuration only from inner ContextConfiguration class. This class describes your context. In this context you created only service bean and no repository. So the only bean that will be created is SessionService. You should add description of SessionRepository in inner ContextConfiguration. Your JpaConfig class will not be used in test class(you don't specify this), only in application.

Categories