I did some research on how to test my DAO layer using HyperSQL and I found this question: https://softwareengineering.stackexchange.com/questions/219362/how-to-test-the-data-access-layer
How do I import DBConnection because I tried using hypersql jar and it did not work. In the question from the link I saw this db.url=jdbc:hsqldb:file:src/test/resources/testData;shutdown=true; but I do not know how to use it.
Using JDBC and Spring, in your application you'd be creating a DataSource object, and using that object to create a JdbcTemplate object, which is what you're using to run your JDBC queries. Similar to this example. So in order to use the HSql database, you'd need to change the values used to setup the DataSource bean but only for your unit tests.
Depending on how you're using Spring (annotations, xml config), there's a few ways of doing it. You'll need to create a new configuration bean, or configuration xml file, and then reference it in your unit tests.
For XML, copy over your spring context xml file, and change the DataSource values to something like:
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
<property name="url" value="jdbc:hsqldb:file:src/test/resources/testData;shutdown=true;"/>
<property name="username" value="SA"/>
<property name="password" value=""/>
You'll then need to reference this new context config in your unit test. Something like:
#RunWith(SpringJUnit4ClassRunner.class)
// ApplicationContext will be loaded from "classpath:/test-config.xml"
#ContextConfiguration("/test-config.xml")
public class DAOTests {
#Autowired
private DatabaseDAO dao;
}
With annotations, you'll need to make a new configuration class (you should have one already, a class annotated with #Configuration) and return the appropriate DataSource. Something like this:
#Bean
public DataSource hsqlDataSource(){
return DataSourceBuilder
.create()
.driverClassName("org.hsql.jdbcDriver")
.url("jdbc:hsqldb:file:src/test/resources/testData;shutdown=true;")
.username("SA")
.password("");
.build();
}
You can setup a profile for your configuration (using #Profile("test") annotation), and then specify which profile you want to use in your unit test (#ActiveProfiles("test")). More information here.
Alternatively, you could use the Spring EmbeddedDatabaseBuilder if you're just trying to do a simple DAO Test. There's a tutorial here. It sets you up with a HSql in memory database (as opposed to a file based one), which is very convenient for setting up / tearing down. Just add the bean to your configuration class, and your DAO should pick it up if Spring is injecting the DataSource for you. Example below taken from here
#Bean
public DataSource dataSource() {
// no need shutdown, EmbeddedDatabaseFactoryBean will take care of this
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("db/setup_script.sql")
.build();
}
There's a bit to Spring configuration management. I haven't covered it in much detail in this answer, but look up a few examples and it will start to make sense.
Related
I'm trying to write a unit test for a Controller in a Spring Boot application. The application runs smoothly, my problem is with running its tests.
Here is the test code:
#RunWith(SpringRunner.class)
#WebMvcTest(MyController.class)
#AutoConfigureTestEntityManager
public class MyControllerTest {
#Autowired
private MockMvc mockMvc;
#Mock
private MyRepository myRepository;
#Mock
ZendeskNotifier zendeskNotifier;
#Mock
ActivityLogger activityLogger;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
#Test
public void cannotSendFooWithoutMessageBody() throws Exception {
this.mockMvc.perform(post("/api/v1/foo/1/send"))
.andDo(print())
.andExpect(status().is4xxClientError())
.andExpect(content().string(containsString("The message body cannot be empty.")));
}
}
When I try running it I get:
***************************
APPLICATION FAILED TO START
***************************
Description:
Field jobEventRepository in foo.bar.util.memsource.svc.MemsourceEventProcessor required a bean named 'entityManagerFactory' that could not be found.
Action:
Consider defining a bean named 'entityManagerFactory' in your configuration.
And that feels weird to me, since I'm providing the AutoConfigureTestEntityManager annotation and would expect all the EntityManager-related stuff to be in place.
If Google brought you here and you're using Spring Boot, you may need to add #DataJpaTest to your test class. It's found in org.springframework.boot:spring-boot-test-autoconfigure. You may also discover upon rerunning that you need to declare a dependency on org.hibernate:hibernate-validator.
Inject Spring's TestEntityManager in your mockito test class.
#Autowired
private TestEntityManager entityManager;
You are already using #AutoConfigureTestEntityManager on the test class to auto configure this test entity manager. So you don't have to do anything else in the config file.
Spring Boot is loading your application's configuration, causing your data layer to be initialized.
Excerpt from Spring Boot's docs, Detecting Test Configuration:
Spring Boot’s #*Test annotations search for your primary configuration
automatically whenever you do not explicitly define one.
The search algorithm works up from the package that contains the test
until it finds a class annotated with #SpringBootApplication or
#SpringBootConfiguration. As long as you structured your code in a
sensible way, your main configuration is usually found.
When the scanning hits your main class it is likely finding an annotation like #EnableJpaRepositories, which initializes your data layer and thus requires the entity manager factory. (You may see other side effects too, like Hibernate initializing your in-memory database if your application uses one.)
As other answers suggest, you could initialize the data layer, or try to wire/mock the missing beans. But since you aren't testing the data layer here it would be better to control the test's configuration.
The docs suggest some solutions:
Move #EnableJpaRepositories from the main class to a config class in child package. It will get scanned by the application (from the top package down) but not by the unit tests. This is discussed in the section User Configuration and Slicing.
Add a nested #Configuration class to override the configuration used by the unit test.
The first one seems like a good rule to follow in general. Other annotations like #EnableBatchProcessing and #EnableScheduling can be treated the same way, which will speed up your unit tests.
you need to configure entityManagerFactory, you can refer bellow code
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="packagesToScan" value="org.demo.test.entity" />
<property name="dataSource" ref="dataSource" />
<property name="jpaProperties">
<props>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">create</prop>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
</props>
</property>
<property name="persistenceProvider">
<bean class="org.hibernate.jpa.HibernatePersistenceProvider">
</bean>
</property>
</bean>
Move #EnableJpaRepositories from the main class to a config class in a child package. It will get scanned by the application (from the top package down) but not by the unit tests.
#Configuration
#EnableJpaRepositories(
value = "com.company.repository"
)
public class JpaConfig {
}
I found out that there is an Annotation you can use to add JPA-Support to your WebMVCTest (#AutoConfigureDataJpa)
#ContextConfiguration(classes = { SurchargesTestConfiguration.class })
#WebMvcTest(SurchargesApiController.class)
#AutoConfigureDataJpa
#ActiveProfiles("local")
class SurchargesApiControllerTest {
#Autowired
private MockMvc mvc;
#Test
void testNotFound() throws Exception {
mvc.perform(get("/surcharges/marketnumber") //
.headers(getDefaultHeaders())) //
.andExpect(status().isNotFound());
}
}
When we using spring jdbc , first we define a dataSource bean and inject it when creating jdbcTemplate object . What I want to know is do we need to define this dataSource in prototype scope. Unless there is only one dataSource object for whole application . I think this affects to reduce application performance.
Here is how I have defined dataSouce inside spring configuration file.
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource" >
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/testdb" />
<property name="username" value="root" />
<property name="password" value="123" />
</bean>
In my DAO class I have autowired dataSOurce as below.
#Repository
public class RecordDAOImpl {
JdbcTemplate jdbcTemplate = null;
#Autowired
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
}
Let me know what is the best way to define dataSource for spring mvc web application.
What I want to know is do we need to define this dataSource in prototype scope
No we don't need. I guess it wouldn't be good idea, we can use some kind of connection pool datasource and singleton scope bean.
We can also have multiple databases and provide for each own datasource singleton scoped, there is not any problem with that.
Let me know what is the best way to define dataSource for spring mvc web application.
There is nothing wrong with defining your data Sources in xml files (although many devs seem to avoid xml). I like to do it using java config, since I feel like its easier to read.
Depending on driver and database it would look more or less like that:
#Configuration
class DatasourceConfig {
#Bean
DataSource datasource() {
PGPoolingDataSource dataSource = new PGPoolingDataSource();
dataSource.setPassword("pass");
dataSource.setPortNumber(123);
dataSource.setUser("user");
dataSource.setMaxConnections(10);
return dataSource;
}
}
I am mostly using #Autowired and #Component annotations in my project. However, I am going to use DataSource class for database actions.
So, I use this is in my dispatcher-servlet.xml :
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/market"/>
<property name="username" value="root"/>
<property name="password" value=""/>
</bean>
In my dao class, My setter for the dataSource is :
#Autowired
private DataSource dataSource;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
this.jdbcTemplateObject = new JdbcTemplate(dataSource);
}
However this is not doing the trick. My jdbcTemplateObject is null.
If I dont use "context:component scan ..." and use classical spring beans instead, without utilizing #Autowired annotation, all works good.
I can use my database. However, I dont want to declare all the beans one by one in my xml file. As the project grows, it is not going to be practical. How can I solve this problem ? Is it possible to declare dataSource in my dispatcher-servlet.xml as component, so #Autowired works on dataSource ?
When you use #Autowired on fields Spring will look for dependencies and inject them right there there is no point if setter method here.
You do not need to worry about how spring is going to inject the dependency. It will take care of complete life cycle.
For more on Spring's Dependecy Injection visit this link.
You have annotated the field with #Autowired which tells spring to inject the dependency directly into the field. If you really want to use the setter annotate the setter with #Autowired instead of the field.
#Autowired
public void setDataSource(DataSource ds) { ... }
However I strongly suggest to not create a JdbcTemplate for each bean that needs one (it is quite heavy to create). The JdbcTemplate is a thread-safe object, once constructed. So instead of creating a new one for each bean that needs one (in the setDataSource method) just create a single JdbcTemplateand inject that.
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
Then in your dao.
#Autowired
private JdbcTemplate jdbcTemplate;
Or what I like to do..
private final JdbcTemplate jdbcTemplate;
#Autowired
public YourRepository(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate=jdbcTemplate;
}
This way you cannot construct an illegal object, whereas with setter based injection you could. While maintaining the possibility to inject one for testing purposes.
Another note, the DriverManagerDataSource is nice for testing but not for production usage, for that use a real connection pool like HikariCP or Tomcat JDBC.
When using #Transactionnal with Spring
How does it select the datasource on which it should open the transaction?
Is there some magic trick like proxies, threadlocals or anything?
If so, do these tricks work with any JDBC library (for Hibernate it works but what about MyBatis?)
What if there are 2 datasources?
And what if i'm calling, in a #Transactionnal service, DAO's with 2 underlying different datasources? Will it be transactionnal for both datasources or just for one of them or will it fail?
Thanks
To use multiple transaction handlers just specifiy a qualifier, and reference it. For two different DAOs with two different datasources, you'll need two different transaction managers. And of course the transactions should be going on your service classes, not DAO's directly. Its the same for any type of transactionmanager be it hibernate or plain old jdbc.
<bean id="transactionManagerOne"
class="org.springframework.orm.hibernate4.HibernateTransactionManager"
p:sessionFactory-ref="sessionFactoryOne">
<qualifier value="One" />
</bean>
and session factory
<bean id="sessionFactoryOne"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"
p:dataSource-ref="dataSourceOne"
and just set up a datasource with the ID dataSourceOne, then you cran reference the transactionmanager in code with the qualifier name :
#Transactional(value = "One")
I can provide a partial answer which is that usually when you use a PlatformTransactionManager from Spring it was associated with a single datasource when it was created. Something like this:
#Bean public PlatformTransactionManager txManager() {
return new HibernateTransactionManager(sessionFactory());
}
The SessionFactory is configured with a datasource. I suspect if you want to have multiple PlatformTransactionManagers then you can't just rely on autowiring them as beans like I did above. You might have to use the TransactionTemplate class instead and code on a slightly lower level.
I have the following in my xml configurations. I would like to convert these to my code because I am doing some unit/integration testing outside of the container.
xmls:
<bean id="MyMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
<property name="configLocation" value="classpath:sql-map-config-oracle.xml"/>
<property name="dataSource" ref="IbatisDataSourceOracle"/>
</bean>
<bean id="IbatisDataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="jdbc/my/mydb"/>
</bean>
code I used to fetch stuff from above xmls:
this.setSqlMapClient((SqlMapClient)ApplicationInitializer.getApplicationContext().getBean("MyMapClient"));
my code (for unit testing purposes):
SqlMapClientFactoryBean bean = new SqlMapClientFactoryBean();
UrlResource urlrc = new UrlResource("file:/data/config.xml");
bean.setConfigLocation(urlrc);
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("oracle.jdbc.OracleDriver");
dataSource.setUrl("jdbc:oracle:thin:#123.210.85.56:1522:ORCL");
dataSource.setUsername("dbo_mine");
dataSource.setPassword("dbo_mypwd");
bean.setDataSource(dataSource);
SqlMapClient sql = (SqlMapClient) bean; //code fails here
when the xml's are used then SqlMapClient is the class that sets up then how come I cant convert SqlMapClientFactoryBean to SqlMapClient
SqlMapClientFactoryBean is a FactoryBean. It does not implement the SqlMapClient interface itself, but manufactures instances of SqlMapClient which are returned when it's getObject() method is called. The Spring container knows about FactoryBeans, and makes them look just like normal beans from the perspective of the caller. I'm not sure that using the FactoryBean outside a container will work - you may get a "not initialized" exception if you call getObject() outside of the container lifecycle ...
Why not create a separate, stripped down Spring config for your test cases, instantiate that, and obtain the beans from there? Alternatively, you could create the SqlMapClient the non-Spring way and set that on your DAO
SqlMapClientFactoryBean factory = new SqlMapClientFactoryBean();
factory.setConfigLocation(YOUR_SQL_MAP_CONFIG_RESOURCE);
factory.afterPropertiesSet(); //omitting try/catch code
client = (SqlMapClient)factory.getObject();
voila
I want to add what worked for me. Had to work with some legacy code and in till I can transition to MyBatis I want to convert the old applicationContext xml to spring #Configuration classes.
#Bean
public SqlMapClient sqlMap() throws Exception
{
SqlMapClientFactoryBean factory = new SqlMapClientFactoryBean();
factory.setConfigLocation(new ClassPathResource("conf/ibatis.xml"));
DataSource dataSource = this.dataSource;
factory.setDataSource(dataSource);
factory.afterPropertiesSet();
return (SqlMapClient) factory.getObject();
}