I have a declarative spring config
#Configuration
public class SpringConfig {
#Bean
public someBean() {
return new Bean1();
}
}
and a #Component annotated Bean
#Component
public class Bean2 {
}
Now I would like to use both of then in my UnitTest
#RunWith(SpringJUnit4ClassRunner.class)
public void UnitTest {
#Autowired Bean1 bean1;
#Autowired Bean2 bean2;
}
but I have no idea how to do it.
You can do this:
#ContextConfiguration(classes = {SpringConfig.class})
#RunWith(SpringJUnit4ClassRunner.class)
public void UnitTest {
#Autowired Bean1 bean1;
#Autowired Bean2 bean2;
}
For class Bean2, you can add the #ComponentScan annotation:
#Configuration
#ComponentScan("com....package.of.bean2")
public class SpringConfig {
#Bean
public someBean() {
return new Bean1();
}
}
If you don't want to add the ComponentScan to your SpringConfig class, you can add an additional test config class with the ComponentScan annotation and add it to the ContextConfiguration annotation:
#ContextConfiguration(classes = {SpringConfig.class, SpringTestConfig.class})
Related
I am using spring-boot-starter-jdbc and trying to use multiple Jdbc DataSources, everything worked when I used named Beans for my JdbcTemplate and then used #Qualifier to inject the right JdbcTemplate into my repository.
Application:
package my.app;
#SpringBootApplication
#EnableAutoConfiguration
public class Application implements CommandLineRunner {
#Autowired
private MyRepository repository;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
public void run(String... args) throws Exception {
List<Stuff> stuff = repository.getStuff();
}
}
Configuration:
package my.app;
#Configuration
#ComponentScan
public class AppConfig {
#Bean(name = "datasource1")
#ConfigurationProperties(prefix = "db1.datasource")
public DataSource dataSource1() {
return DataSourceBuilder.create().build();
}
#Bean(name = "db1")
public DataSource db1(#Qualifier("datasource1" DataSource ds) {
return new JdbcTemplate(ds);
}
#Bean(name = "datasource2")
#ConfigurationProperties(prefix = "db2.datasource")
public DataSource dataSource2() {
return DataSourceBuilder.create().build();
}
#Bean(name = "db2")
public DataSource db1(#Qualifier("datasource1" DataSource ds) {
return new JdbcTemplate(ds);
}
}
Repository:
package my.app;
#Repository
public class MyRepository {
private JdbcTemplate db1;
private JdbcTemplate db2;
#Autowired
public class MyRepository(#Qualifier("db1") JdbcTemplate db1, #Qualifier("db2") JdbcTemplate db2) {
this.db1 = db1;
this.db2 = db2;
}
}
When I instantiate MyRepository everything is fine.
I did a little refactoring to try to make new classes for Db1JdbcTemplate and Db2JdbcTemplate so I could inject them without qualifiers. Unfortunately when I do this I get the following exception:
Here's what I attempted to do:
Removed named JdbcTemplate Beans from AppConfig:
package my.app;
#Configuration
#ComponentScan
public class AppConfig {
#Bean(name = "datasource1")
#ConfigurationProperties(prefix = "db1.datasource")
public DataSource dataSource1() {
return DataSourceBuilder.create().build();
}
#Bean(name = "datasource2")
#ConfigurationProperties(prefix = "db2.datasource")
public DataSource dataSource2() {
return DataSourceBuilder.create().build();
}
}
Created 2 new classes for named JdbcTemplates:
package my.app;
#Component
public class Db1JdbcTemplate extends JdbcTemplate {
#Autowired
public Db1JdbcTemplate(#Qualifier("datasource1") DataSource ds1) {
super(ds1);
}
}
package my.app;
#Component
public class Db2JdbcTemplate extends JdbcTemplate {
#Autowired
public Db2JdbcTemplate(#Qualifier("datasource2") DataSource ds2) {
super(ds2);
}
}
Modified MyRepository to use those:
package my.app;
#Repository
public class MyRepository {
private Db1JdbcTemplate db1;
private Db2JdbcTemplate db2;
#Autowired
public class MyRepository(Db1JdbcTemplate db1, Db2JdbcTemplate db2) {
this.db1 = db1;
this.db2 = db2;
}
}
So I have #Component annotations on both of those so I expected to be able to be registered and be able to be Autowired. However when I run things I get the following exception:
Error creating bean with name 'repository' defined in file
[/Users/me/spring/target/classes/my/app/MyRepository.class]:
Unsatisfied dependency expressed through constructor parameter 0;
nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type 'my.app.Db1JdbcTemplate' available: expected
at least 1 bean which qualifies as autowire candidate. Dependency
annotations: {}
Any ideas what I need to change or how I can debug further? I can always go back to the working version but I'd prefer to be more explicit when possible so the compiler can help me out instead of runtime errors caused by a typo'd Qualifier name.
A more standard way of doing this and avoid typos in qualifier names is to avoid using qualifier names, and instead use Qualifier annotations:
#Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Qualifier
public #interface DB1 {
}
#Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Qualifier
public #interface DB2 {
}
and then, when defining your beans:
#Configuration
#ComponentScan
public class AppConfig {
#Bean
#ConfigurationProperties(prefix = "db1.datasource")
#DB1
public DataSource dataSource1() {
return DataSourceBuilder.create().build();
}
#Bean
#DB1
public JdbcTemplate db1(#DB1 DataSource ds) {
return new JdbcTemplate(ds);
}
#Bean
#ConfigurationProperties(prefix = "db2.datasource")
#DB2
public DataSource dataSource2() {
return DataSourceBuilder.create().build();
}
#Bean
#DB2
public JdbcTemplate db1(#DB2 DataSource ds) {
return new JdbcTemplate(ds);
}
}
And finally when injecting your beans:
public class MyRepository(#DB1 JdbcTemplate db1, #DB2 JdbcTemplate db2) {
...
}
If the main concern is typos, just define your names as constants (something like public static final String DATASOURCE1 = "datasource1";) and consistently use those in your qualifier annotations instead of string literals. No need to add new classes or interfaces.
I want to InjectMocks into my interface and not my service class , so far its not possible with Mockito (I am using Mockito 2.8.9). I have used SpringBoot and using #MockBean I am able to test interfaces of services but with #Mock of SpringMvc I can only test a concrete class, why and what can I do to test services?
Any suggestion on how to get this to interface level.
#RunWith(MockitoJUnitRunner.class)
#ContextConfiguration(classes = EmployeeServiceImpl.class)
public class EmployeeServiceTest {
#org.mockito.Mock
private EmployeeDao employeeDao;
#InjectMocks
private EmployeeServiceImpl employeeService ;
#Before
public void initMocks() {
MockitoAnnotations.initMocks(this);
}
#Test
public void SaveEmployee() throws Exception {
Employee employee = new Employee();
employee.setEmployeeName("valentine");
employee.setSalary(400);
employee.setDepartmentId(1);
employee.setEmployeeId(1);
Employee employee1 ;
when(employeeDao.addEmployee(employee)).thenReturn(employee);
employee1 = employeeService.saveEmployee(employee);
org.junit.Assert.assertNotNull(employee);
assertEquals(employee1.getSalary(), employee.getSalary());
Mockito.verify(employeeDao).addEmployee(employee);
}
So i solved this by first creating Bean in Config class that returns interface of Dao
#Profile("test")
#Configuration
public class Config {
#Bean
#Primary
public EmployeeDao employeeDao(){
return Mockito.mock(EmployeeDao.class);
}
}
Then i used the profile in my test and set configContxt TO my Config Class
#ActiveProfiles("test")
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes ={ EmployeeServiceImpl.class, Config.class})
public class EmployeeServiceTest1 {
#Autowired
private EmployeeDao employeeDao;
#Autowired
private EmployeeService employeeService;
I have a config class that provides two implemenations of the same base bean interface. I want these to be set on the autowired fields conditionally based on an annotation on the field.
public class MyController
{
#Autowired
private MyBeanInterface base;
#Autowired
#MyAnnotation
private MyBeanInterface special;
}
This is a pesudo-code of the config class:
#Configuration
public class ConfigClass
{
#Bean
#Primary
public MyBeanInterface getNormalBeanInterface()
{
return new MyBeanInterfaceImpl();
}
#Bean
//This doesn't work
#ConditionalOnClass(MyAnnotation.class)
public MyBeanInterface getSpecialBeanInterface()
{
return new MyBeanInterfaceForMyAnnotation();
}
}
How can I make the annotated field be populated by the second bean?
Use Qualifier annotation. Example:
Controller:
Add Qualifier annotation at the injected fields with bean id as parameter:
public class MyController
{
#Autowired
#Qualifier("normalBean")
private MyBeanInterface base;
#Autowired
#Qualifier("specialBean")
private MyBeanInterface special;
}
ConfigClass
Specify bean id:
#Configuration
public class ConfigClass
{
#Bean(name="normalBean")
#Primary
public MyBeanInterface getNormalBeanInterface()
{
return new MyBeanInterfaceImpl();
}
#Bean(name="specialBean")
public MyBeanInterface getSpecialBeanInterface()
{
return new MyBeanInterfaceForMyAnnotation();
}
}
I have two datasources that are equal in their structure but not in their Data.
My application has to deal with both of them at the same time.
I have controller, servie, dao structure that looks like this.
Controller Modell:
#Controller
public abstract class MyFancyControllerModell{
private MyService service;
public MyFancyControllerModell (MyService service){
this.service = service;
}
#RequestMapping(....)
public void editSomeData(String data){....}
}
Controller implementation:
#Controller
#RequestMapping(...)
public class MyControllerImpl1 extends MyFancyControllerModell{
#Autowired
public MyControllerImpl1(#Qualifier("myServiceInstance1") MyService service){
super(service);
}
}
#Controller
#RequestMapping(...)
public class MyControllerImpl2 extends MyFancyControllerModell{
#Autowired
public MyControllerImpl2(#Qualifier("myServiceInstance2") MyService service){
super(service);
}
}
And the Service:
public class MyService{
private MyDao myDao;
public MyService(MyDao myDao){
this.myDao = myDao;
}
#Transactional
public void editSomeData(String data){...}
}
I create the Beans in my configuration class like this:
private DataSource lookupDataSource(final String jndiName) {
final JndiDataSourceLookup dsLookup = new JndiDataSourceLookup();
dsLookup.setResourceRef(true);
return dsLookup.getDataSource(jndiName);
}
#Bean
public DataSource dataSource1(){
return lookUpDataSource("dataSource1");
}
#Bean
public DataSource dataSource2(){
return lookUpDataSource("dataSource2");
}
#Bean
public MyService myServiceInstance1(#Qualifier("dataSource1") DataSource dataSource){
return new(MyService(new MyDao(dataSource))));
}
#Bean
public MyService myServiceInstance1(#Qualifier("dataSource1") DataSource dataSource){
return new(MyService(new MyDao(dataSource)));
}
My question is, is it possible to create transactionmanagers for both datasources without the need to declare in the service layer which transactionmanager is used?
I tried creating them as bean just like the services but that did not work.
Check out here the answers for previous quesion related to transaction manager...
https://stackoverflow.com/a/1961853/7504001
I have a ConfigurationProperties class and want to test it using junit. But the object is always null. What might be missing in the following code?
#EnableAutoConfiguration
#ComponentScan
#EnableConfigurationProperties(MyProperties.class)
public class AppConfig {
}
#Service
public class MyService {
#Autowired
private MyProperties props;
public void run() {
props.getName();
}
}
#Component
#ConfigurationProperties(prefix = "my")
public class MyProperties {
private String name;
//getter,setter
}
application.properties:
my.name=test
test:
#Configuration
#ComponentScan(basePackageClasses = {MyService.class, MyProperties.class},
includeFilters = #ComponentScan.Filter(value = {MyService.class, MyProperties.class},
type = FilterType.ASSIGNABLE_TYPE),
lazyInit = true
)
#PropertySources(
#PropertySource("application.properties")
)
class AppTest {
#Bean
public static PropertySourcesPlaceholderConfigurer propertiesResolver() {
return new PropertySourcesPlaceholderConfigurer();
}
}
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = ApplicationConfigTest.class)
public class MyTest extends AbstractJUnit4SpringContextTests {
#Autowired
private MyService service;
#Test
public void testService() {
service.run();
}
}
The following will load it for you:
#ContextConfiguration(classes = Application.class, initializers = ConfigFileApplicationContextInitializer.class)