Mockito - How to avoid inserting data into DB? - java

How can we write mockito for the below code? It's been written in normal JDBC. I need to create a mock of all this code having main method (which is driving all the logic of updating the data).
I am really need help in mocking the avoid inserting the actual data. Could someone please guide me ?
public class PaytPaytmBilling {
private static Category logger = Category.getInstance(PaytPaytmBilling.class);
private static InputStream inputS = XY.class.getResourceAsStream("/paytm.properties");
private static final INSERT_QUERY = "INSERT STATEMENT";
private static void insertPaytPaytmBilling(ArrayList allPaytPaytmBill) throws Exception{
conn = getConnection(userId, passwd, prop.getProperty("databaseURL"));
String childSql = buildInsertPaytPaytmBillSql();
PreparedStatement pStatement = conn.prepareStatement(childSql);
for (int i=0; i<allPaytPaytmBill.size(); i++){
PaytPaytmBill PaytmBill = (PaytPaytmBill) allPaytPaytmBill.get(i);
pStatement.setString(1, PaytmBill.getXX());
pStatement.setString(2, PaytmBill.getYY());
pStatement.setString(3, PaytmBill.getAA());
pStatement.setLong(4, PaytmBill.getBB());
pStatement.setLong(5, PaytmBill.getCC));
pStatement.setString(6, PaytmBill.getDD());
pStatement.setInt(7, PaytmBill.getEE());
pStatement.setInt(8, PaytmBill.getFF());
pStatement.setString(9, "");
pStatement.setString(10, "");
pStatement.execute();
}
pStatement.close();
conn.close();
}
private static void getDbConn() throws Exception {
// Here get DB connection
}
public static void main(String[] args) throws Exception
{
ArrayList allPaytPaytmBill = new ArrayList();
XY.init();
getDbConn();
// This query reads data from other tables and creates the data..
String qmrString = qmr.buildQmrSql();
allPaytPaytmBill = qmr.getAllMemberData(qmrString);
insertPaytPaytmBilling(allPaytPaytmBill);
}
}
Mockito Test class:
#RunWith(MockitoJUnitRunner.class)
public class PaytmBillingTest {
private static Category logger = Category.getInstance(PaytmBillingTest.class);
#Mock
private DataSource ds;
#Mock
private Connection c;
#Mock
private PreparedStatement stmt;
#Mock
private ResultSet rs;
private ArrayList<PaytmBill> allPaytmBill;
#Before
public void before() {
allPaytmBill = new ArrayList<>();
PaytmBill PaytmBill = new PaytmBill();
PaytmBill.setAA("1182");
PaytmBill.setBB("5122");
PaytmBill.setCC("201807");
PaytmBill.setDD(0L);
PaytmBill.setEE(100);
PaytmBill.setFF(0);
PaytmBill.setGG(0);
PaytmBill.setHH("A");
PaytmBill.setII(null);
PaytmBill.setJJ(null);
allPaytmBill.add(PaytmBill);
}
#Test
public void testPaytmBilling() {
PaytmBilling PaytmBilling = new PaytmBilling();
}
}

First of all, it looks like you are not showing use the real code. For example you added private static void getDbConn() but the code calls conn = getConnection(...), the variable conn is not declared anywhere, etc. This makes it harder to really help with your issue.
Looking at your unit test, you want to mock instances of certain classes used by PaytPaytmBilling, like DataSource, Connection and PreparedStatement. These are called 'dependencies'.
In order to do that, you need to change PaytPaytmBilling so that these dependencies are 'injected' (see Dependency Injection). This means they are provided to PaytPaytmBilling via the constructor or a setter (or with some frameworks just by adding an annotation on the field).
In the current code, the dependencies are obtained by PaytPaytmBilling itself (e.g. by calling a static method, or creating a new instance) and they cannot be mocked (except via some black magic mocking frameworks which I don't advise you to get into right now).
To write good unit tests, you need to write (or refactor) the code to be testable, which means dependencies are injected, not obtained internally in the class. Also avoid static methods and data (constants are ok), they don't play nice with dependency injection and testable code.
So for example the DataSource could be injected via the constructor like this:
public class PaytPaytmBilling {
private static final String CHILD_SQL = "SELECT bladiebla...";
private DataSource dataSource;
public PaytPaytmBilling(DataSource dataSource) {
this.dataSource = dataSource;
}
public void insertPaytPaytmBilling(List<PaytmBill> allPaytPaytmBill) {
// keeping the example simple here.
// don't use String literals for the parameters below but read
// them from Properties (which you can mock for the unit test)
Connection conn = dataSource.getConnection("userId", "passwd", "url");
PreparedStatement pStatement = conn.prepareStatement(CHILD_SQL);
for (int i=0; i<allPaytPaytmBill.size(); i++){
PaytPaytmBill PaytmBill = (PaytPaytmBill) allPaytPaytmBill.get(i);
pStatement.setString(1, PaytmBill.getXX());
pStatement.setString(2, PaytmBill.getYY());
pStatement.setString(3, PaytmBill.getAA());
// ...
pStatement.execute();
}
pStatement.close();
conn.close();
}
If you re-write the code like above, you could test it like this:
#RunWith(MockitoJUnitRunner.class)
public class PaytmBillingTest {
// this will cause Mockito to automatically create an instance
// and inject any mocks needed
#InjectMocks
private PaytmBilling instanceUnderTest;
#Mock
private DataSource dataSource;
// connection is not directly injected. It is obtained by calling
// the injected dataSource
#Mock
private Connection connection;
// preparedStatement is not directly injected. It is obtained by
// calling the connection, which was obtained by calling the
// injected dataSource
#Mock
private PreparedStatement preparedStatement;
private List<PaytmBill> allPaytmBill;
#Before
public void before() {
allPaytmBill = new ArrayList<>();
PaytmBill paytmBill = new PaytmBill();
paytmBill.setAA("1182");
paytmBill.setBB("5122");
paytmBill.setCC("201807");
paytmBill.setDD(0L);
paytmBill.setEE(100);
paytmBill.setFF(0);
paytmBill.setGG(0);
paytmBill.setHH("A");
paytmBill.setII(null);
paytmBill.setJJ(null);
allPaytmBill.add(PaytmBill);
}
#Test
public void testPaytmBilling() {
// given
when(dataSource.getConnection(anyString(), anyString(), anyString())).thenReturn(connection);
when(connection.prepareStatement(anyString())).thenReturn(preparedStatement);
// when
instanceUnderTest.insertPaytPaytmBilling(allPaytPaytmBill);
// then
verify(pStatement).setString(1, paytmBill.getXX());
verify(pStatement).setString(2, paytmBill.getYY());
verify(pStatement).setString(3, paytmBill.getAA());
// ...
verify(pStatement).execute();
verify(pStatement).close();
verify(connection).close();
}
Unrelated suggestion regarding your code: It's better to close resources in a finally block, or using try-with resources. In you current code resources will not be closed if an exception occurs whilst processing on the resources:
Connection conn = dataSource.getConnection("userId", "passwd", "url");
PreparedStatement pStatement = conn.prepareStatement(childSql);
try {
// processing steps
}
finally {
pStatement.close();
conn.close();
}
Or try-with-resources:
try (Connection conn = dataSource.getConnection("userId", "passwd", "url"),
PreparedStatement pStatement = conn.prepareStatement(childSql)) {
// processing steps
}
Since Connection and PreparedStatement implement the AutoCloseable interface they will be closed automatically when the try block ends. This is possible since Java 7.

Related

How to write JUnit test of a class that uses anonymous inner class such as PreparedStatementSetter?

I have the following class that uses an anonymous inner class (new PreparedStatementSetter()) to set values in the query using a PreparedStatementSetter and returns a List of object.
The problem is that I am not sure how to write unit test for this kind of method.
As soon as my unit test reaches the line where it calls getJdbcTemplate().query(......) it jumps to the end of line and discAuditLogList value returns null and unit test finishes without any errors.
When I look at the code coverage in IntelliJ all the ps.setString statements never got executed.
ProductLogDao.java
//bunch of setter getters here
public List<ProductLogDTO> getProductLogDetail(RequestDTO requestDTO) throws SQLException, DataAccessException {
logger.info("Inside ProductLogDao.getProductLogDetail" + requestDTO);
List<ProductLogDTO> productLogList = null;
final Map<String, String> requestParamMap = requestDTO.getParameters();
if ("custom".equalsIgnoreCase(requestParamMap.get("recordtype"))) {
ProductLogList = getProductCustomDetail(senderFeeSql, requestParamMap);
return ProductLogList;
}
return productLogList;
}
public List<ProductLogDTO> getProductCustomDetail(String senderFeeSql,
final Map<String, String> requestParamMap) throws SQLException {
#SuppressWarnings("unchecked")
List<ProductLogDTO> productLogList = getJdbcTemplate().query(senderFeeSql, new PreparedStatementSetter() {
public void setValues(PreparedStatement ps) throws SQLException {
/***************************************************************************************************************/
//FOLLOWING STATMENTS NEVER GET EXECUTED BECAUSE THEY ARE WITHIN ANONYMOUS CLASS (new PreparedStatementSetter())
/***************************************************************************************************************/
ps.setString(1, requestParamMap.get("startDfId"));
ps.setString(2, requestParamMap.get("startDfId"));
ps.setString(3, requestParamMap.get("chanelId"));
ps.setString(4, requestParamMap.get("chanelId"));
ps.setString(5, requestParamMap.get("fromDateTime"));
ps.setString(6, requestParamMap.get("toDateTime"));
ps.setString(7, requestParamMap.get("fromDateTime"));
ps.setString(8, requestParamMap.get("toDateTime"));
}
}, new ProductCustomRowMapper());
if (null != productLogList && (productLogList.size() > 0)) {
productLogList.get(0).setRecordtype("custom");
productLogList.get(0).setRecordsFetched(productLogList.get(0).getRecordsFetched());
}
return productLogList;
}
ProductLogDaoTest.java
public class ProductLogDaoTest {
ProductLogDao instance = new ProductLogDao();
RequestDTO requestDTO = Mockito.mock(RequestDTO.class);
JdbcTemplate jdbcTemplate = Mockito.mock(JdbcTemplate.class);
Map<String, String> requestParamMap = new HashMap<>();
#Before
public void setUp() throws Exception {
instance.setJdbcTemplate(jdbcTemplate);
}
#Test
public void getProductLogDetail_W_Custom() throws SQLException, DataAccessException {
when(requestDTO.getParameters()).thenReturn(requestParamMap);
requestParamMap.put("recordtype", "custom");
assertNotNull(instance.getProductLogDetail(requestDTO));
}
}
Never create an object instance inside in the dependent object like this: new PreparedStatementSetter()
It wires this class instance hardly and you unable to change it to a mock.
Always do one of these:
Create these class instances by a factory object method call (myBusinessFactoryInstance.createPreparedStatementSetter). Than you can create a mock if you change the factory.
In Spring you can inject this dependency by constructor or setter method.
If you do so your testing will be like a charm.
You have mocked JdbcTemplate, but haven't mocked your JdbcTemplate methods. That's why it returns null.
You can use some kind of test database. In case of Spring you can use embedded ones: https://docs.spring.io/spring/docs/3.0.0.M4/spring-framework-reference/html/ch12s08.html. Please refer to this question for more information about simulating database during tests: How to simulate a DB for testing (Java)?
Either way you can check whether or not your query() method was called with Mockito's verify() method.

JPA: How to set MySQL session variables?

I need to set a mysql session variable for my application to work with a MariaDB Galera Cluster as expected. The SQL call is: SET SESSION wsrep_sync_wait = 1. It shall be set at all times when the application uses the database. I am using EclipseLink as the JPA provider.
My question is: What is the best way to achieve this?
Option 1: EclipseLink Session Customizer
Register a session customizer in persistence.xml:
public class SessionCustomizerImpl implements org.eclipse.persistence.config.SessionCustomizer {
private final static String WSREP_SYNC_WAIT_CHECK_SQL = "SHOW SESSION VARIABLES LIKE 'wsrep_sync_wait'";
private final static String WSREP_SYNC_WAIT_SET_SQL = "SET SESSION wsrep_sync_wait = 1";
#Override
public void customize(Session session) throws Exception {
Vector result = session.executeSQL(WSREP_SYNC_WAIT_CHECK_SQL);
if ((result != null) && !result.isEmpty()) {
session.executeNonSelectingSQL(WSREP_SYNC_WAIT_SET_SQL);
// Galera connection detected; wsrep_sync_wait set to 1
} else {
// No Galera connection detected; wsrep_sync_wait not set
}
}
}
This does not work for me. Querying the session variable from an EntityManager returns a value of 0.
Option 2: EntityManager factory
Every time a new EntityManager is created, the SQL is executed.
public class SyncWaitEntityManagerFactory implements Factory<EntityManager> {
private final EntityManagerFactory emf;
#Inject
public SyncWaitEntityManagerFactory(EntityManagerFactory emf) {
this.emf = emf;
}
#Override
public EntityManager provide() {
final EntityManager em = emf.createEntityManager();
// set it
em.getTransaction().begin();
em.createNativeQuery("SET SESSION wsrep_sync_wait = 1").executeUpdate();
em.getTransaction().commit();
return em;
}
#Override
public void dispose(EntityManager instance) {
if (instance.isOpen()) {
instance.close();
}
}
}
This works, but I'm not sure if it is overkill. Also, I am worried about the cost of the transaction, which is only required by Query#executeUpdate(), but not by the actual SQL call.
Option 3: Via JDBC URL
Appending the variable and value to the JDBC URL (see here for details):
String jdbcUrl = "jdbc:mysql://db.example.test:3306/"+ JDBC_DB
+"?sessionVariables=wsrep_sync_wait=1";
Properties p = new Properties();
p.put("javax.persistence.jdbc.url", jdbcUrl);
p.put("javax.persistence.jdbc.user", JDBC_USER);
p.put("javax.persistence.jdbc.password", JDBC_PASSWORD);
EntityManagerFactory emf = Persistence.createEntityManagerFactory("myPU", p);
EntityManager entityManager = emf.createEntityManager();
Nice solution. Works for me; no effort, no transaction necessary. Downside: I can't catch exceptions (example: check first if the variable exists, then set it -- allows deployment of the code on systems that don't support/use this specific variable).
You could also use an aspect to execute a query every time a getConnection() is called which is for every transaction basically (the aspect is set after the call so that we have a valid connection object):
#Component
#Aspect
public class CustomConnectionPreparer implements ConnectionPreparer
{
#AfterReturning(pointcut = "execution(* *.getConnection(..))", returning = "connection")
public Connection prepare(Connection connection) throws SQLException {
// execute the query (also exception handling)
try (Statement statement = connection.createStatement()) {
statement.execute("SET SESSION wsrep_sync_wait = 1");
} catch (SQLException e) {
throw e;
}
return connection;
}
}
And before you return the connection to the caller you execute your query and you should always have that value set.

Does JdbcTemplate create a new connection every time you call query()?

In the below example, does JdbcTemplate create two connections or one?
public class MyDao {
private JdbcTemplate jdbcTemplate;
public List<Data1> getData1() {
return jdbcTemplate.query(mySql, myParams, myCallback);
}
public List<Data2> getData2() {
jdbcTemplate.query(mySql2, myParams2, myCallback2);
}
}
public class Main {
public static void main(String[] args) {
MyDao dao = new MyDao();
List<Data1> d1 = dao.getData1();
List<Data2> d2 = dao.getData2();
doStuff(d1, d2);
}
}
That is to say, does it reuse the connection from the first query? We are assuming that it was constructed with a basic data source (not a pooled data source).
It depends on the JdbcTempate's DataSource. If you provided a connection pool, like Apache commons-dbcp, then DBCP will do its best to reuse Connections. If you used Spring JDBC's DriverManagerDataSource a new Connection will be created / closed on each JdbcTemplate.query call.

How to run arbitrary sql with mybatis?

I've an application that use mybatis for object persistence. But there are chances I need to run arbitrary sql(from user). Can I do it with mybatis?
Update:
I choose to use dbutils (JDBC) to run user-defined sql, but I need a instance of DataSource to create QueryRunner. Is there any way I can get datasource from mybatis?
I use this utilitary class:
import java.util.List;
import org.apache.ibatis.annotations.SelectProvider;
public interface SqlMapper {
static class PureSqlProvider {
public String sql(String sql) {
return sql;
}
public String count(String from) {
return "SELECT count(*) FROM " + from;
}
}
#SelectProvider(type = PureSqlProvider.class, method = "sql")
public List<?> select(String sql);
#SelectProvider(type = PureSqlProvider.class, method = "count")
public Integer count(String from);
#SelectProvider(type = PureSqlProvider.class, method = "sql")
public Integer execute(String query);
}
Your question is similar to How to exequte query directly from java code using mybatis?
I have already given the answer to that question. But I hope this solution will help you.
Mybatis has already this function, but you must use the adapter as follows.
create an adapter class;
public class SQLAdapter {
String sql;
public SQLAdapter(String sql) {
this.sql = sql;
}
public String getSql() {
return sql;
}
public void setSql(String sql) {
this.sql = sql;
} }
create typeAlias of class SQLAdapter
<typeAlias alias="sqladapter" type="com.zj.xxx.xxx.SQLAdapter" />
put select tag in each object xml where you need to execute the sql directly.
<select id="findRecords" parameterType="SQLAdapter" resultMap="xxxxxResultMap">
${sql}
</select>
call this select method like
String _sql = "select * from table where... order by... limit...";
xxxxx.findRecords(new SQLAdapter(_sql));
Things have been all done. you can no longer write complex sql language in the xml file. Good Luck.
Based on the answers provided, they both are good. But both of them required an Adapter class to be used.
Using Mybatis version 3, I succeeded using a HashMap<String, String> to keep and pass the SQL.
See the codes below.
in Mapper class
final String sql = "${sql}";
#Select(sql)
void execute(HashMap<String, String> m);
when invoke the method:
String sql = "SELECT * FROM record limit 1";
HashMap<String, String> map = new HashMap<String, String>();
map.put("sql", sql);
mapper.execute(map);
HashMap provides a way that you don't have to define the Class properties, or fields in code, you can use a Map to define it redomly.
Thanks.
Reusable fragment of SQL can be used to create select part of query dynamically. In you mapper pass query as normal parameter:
#Param("sql")String sql
In your query just access the parameter using ${sql} instead of #{sql}.
Value in parameter sql can be a fully valid sql query or a fragment of sql query.
For testing I use
import org.apache.ibatis.jdbc.ScriptRunner;
import java.io.Reader;
import java.io.StringReader;
public class test {
private static final String conf = "mybatis.conf.xml";
private SqlSessionFactoryBuilder builder;
private SqlSessionFactory sessionFactory;
Reader reader;
private SqlSession session;
private ScriptRunner runner;
#Before
public void before() {
builder = new SqlSessionFactoryBuilder();
try {
reader = Resources.getResourceAsReader(conf);
} catch (IOException e) {
e.printStackTrace();
}
sessionFactory = builder.build(reader);
session = sessionFactory.openSession();
runner = new ScriptRunner(session.getConnection());
runner.setAutoCommit(true);
runner.setStopOnError(true);
}
#Test
public void testSelectChapelStatus() {
Reader populate = new StringReader("insert into person values (7553,0,'201002496','Wish','Jill','Rain',1,0,NULL,'xxx#LCU.EDU');\r\n"
+ "");
runner.runScript(populate);
}

How to use SQLErrorCodeSQLExceptionTranslator and DAO class with #Repository in Spring?

I'm using Spring 3.0.2 and I have a class called MovieDAO that uses JDBC to handle the db. I have set the #Repository annotations and I want to convert the SQLException to the Spring's DataAccessException I have the following example:
#Repository
public class JDBCCommentDAO implements CommentDAO {
static JDBCCommentDAO instance;
ConnectionManager connectionManager;
private JDBCCommentDAO() {
connectionManager = new ConnectionManager("org.postgresql.Driver", "postgres", "postgres");
}
static public synchronized JDBCCommentDAO getInstance() {
if (instance == null)
instance = new JDBCCommentDAO();
return instance;
}
#Override
public Collection<Comment> getComments(User user) throws DAOException {
Collection<Comment> comments = new ArrayList<Comment>();
try {
String query = "SELECT * FROM Comments WHERE Comments.userId = ?";
Connection conn = connectionManager.getConnection();
PreparedStatement stmt = conn.prepareStatement(query);
stmt = conn.prepareStatement(query);
stmt.setInt(1, user.getId());
ResultSet result = stmt.executeQuery();
while (result.next()) {
Movie movie = JDBCMovieDAO.getInstance().getLightMovie(result.getInt("movie"));
comments.add(new Comment(result.getString("text"), result.getInt("score"), user, result.getDate("date"), movie));
}
connectionManager.closeConnection(conn);
} catch (SQLException e) {
e.printStackTrace();
//CONVERT TO DATAACCESSEXCEPTION
}
return comments;
}
}
I Don't know how to get the Translator and I don't want to extends any Spring class, because that is why I'm using the #Repository annotation
You must provide a bean-post processor to get your goal
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
Or use SQLExceptionSubclassTranslator
private SQLExceptionTranslator sqlExceptionTranslator = new SQLExceptionSubclassTranslator();
catch(SQLException e) {
throw sqlExceptionTranslator.doTranslate("<WHICH_TASK>", "<WHICH_SQL_QUERY>", e);
}
instead
JDBC doesn't work very well with #Repository and automatic exception translation, because SQLException is not a runtime exception. #Repository-style exception translation only really works with data access APIs that use runtime exceptions, e.g. Hibernate and JPA.
The #Repository annotation is used by the Spring context to auto-generate a proxy wrapper around your DAO, translating the exceptions as they get thrown. This only works with runtime exceptions, though. Specifically, if your DAO implementation class methods throw SQLException, then so must your interface method signatures, and so must the proxy wrapper, and so the client code must handle that exception, which all rather defeats the point of exception translation.
For JDBC, some coupling to the Spring API is usually necessary, either by extending JdbcDaoSupport and using getExceptionTranslator(), or manually constructing your own SQLExceptionTranslator instance. Either way, you need to catch SQLException inside the DAO and translate it into a DataAccessException.
catch (SQLException e) {
throw new DataAccessException("some message",e);
}

Categories