How to dynamically append results to a mocked method in Mockito - java

I'm originally mocking java.sql.ResultSet like this:
ResultSet rs = mock(ResultSet.class);
when(rs.next()).thenReturn(true, false);
when(rs.getString(1)).thenReturn("foo");
when(rs.getString(2)).thenReturn("bar");
when(rs.getInt(3)).thenReturn(55);
The above code is for mocking a simple one level data mocking, and it's working without any issues.
However, we have more complicated cases in our code where we use ResultSet in multiple method calling levels. Here's a sample code for a nested calling for ResultSet.
void level1(){
ResultSet rs = stmt.executeQuery();
while (rs.next()) {
String a = rs.getString(1);//Expected to return "foo"
String b = rs.getString(2);//Expected to return "bar"
int c = rs.getInt(3);//Expected to return 55
}
level2();
}
void level2(){
ResultSet rs = stmt.executeQuery();
while (rs.next()) {
String a = rs.getString(1);//Expected to return "lorem"
String b = rs.getString(2);//Expected to return "ipsum"
}
level3();
}
void level3(){
ResultSet rs = stmt.executeQuery();
while (rs.next()) {
String a = rs.getString(1);//Expected to return "alice"
int b = rs.getInt(2);//Expected to return 66
int c = rs.getInt(3);//Expected to return 77
}
level3();
}
To mock the method level1 in the above snippet, we have to write something similar to this
ResultSet rs = mock(ResultSet.class);
when(rs.next()).thenReturn(true, false, true, false, true, false);
when(rs.getString(1)).thenReturn("foo", "lorem", "alice");
when(rs.getString(2)).thenReturn("bar", "ipsum");
when(rs.getInt(3)).thenReturn(55, 77);
when(rs.getInt(2)).thenReturn(66);
As you can see from the above example, mocking the nested methods is not readable at all.
We are looking for a way to replace the unreadable mocking code with something more redable that will look like this:
ResultSet rs = mock(ResultSet.class);
when(rs.next()).thenReturnAndAppend(true, false);
when(rs.getString(1)).thenReturnAndAppend("foo");
when(rs.getString(2)).thenReturnAndAppend("bar");
when(rs.getInt(3)).thenReturnAndAppend(55);
when(rs.next()).thenReturnAndAppend(true, false);
when(rs.getString(1)).thenReturnAndAppend("lorem");
when(rs.getString(2)).thenReturnAndAppend("ipsum");
when(rs.next()).thenReturnAndAppend(true, false);
when(rs.getString(1)).thenReturnAndAppend("alice");
when(rs.getInt(2)).thenReturnAndAppend(66);
when(rs.getInt(3)).thenReturnAndAppend(77);
Is there a way in Mockito to achieve that?
I've already tried to use when().then() multiple times for the same method, but that overrides the previous mock for that method, and doesn't append values.
I've also tried to use OngoingStubbing to accomplish that but it throws an exception saying this is a bad behavior to use then on different lines.
I've also tried to create a custom mock method to store values in a map and load them from the map later
private Map<Object, Stream> mockingMap;
protected <T> void whenThen(T method, T value) {
if(!mockingMap.containsKey(method)) {
mockingMap.put(mock, (Stream<T>)Stream.of());
}
mockingMap.put(method, Stream.concat(mockingMap.get(method), Stream.of(value)))
when(method).thenAnswer(e -> {
mockingMap.get(method).next()
});
}
And will use this method like this:
whenThen(rs.getString(1), "foo");
whenThen(rs.getString(1), "lorem");
The issue with this implementation is that the value of method in whenThen method doesn't represent the mocked method rs.getString(1), which means that calling rs.getString(1) two times will result in two different values for the method parameter.
My first question is: Is there any built in method in Mockito that has a similar behavior to thenReturnAndAppend.
My second question is: If there's no similar method to thenReturnAndAppend, then how can I pass a unique key to the whenThen method that represents the method that we are trying to mock?

According to the documentation, you can do so using iterator-style stubbing:
https://javadoc.io/doc/org.mockito/mockito-core/2.24.5/org/mockito/Mockito.html#stubbing_consecutive_calls
Stubbing consecutive calls (iterator-style stubbing) Sometimes we need to stub with different return value/exception for the same method
call. Typical use case could be mocking iterators. Original version of
Mockito did not have this feature to promote simple mocking. For
example, instead of iterators one could use Iterable or simply
collections. Those offer natural ways of stubbing (e.g. using real
collections). In rare scenarios stubbing consecutive calls could be
useful, though:
when(mock.someMethod("some arg"))
.thenThrow(new RuntimeException())
.thenReturn("foo");
//First call: throws runtime exception:
mock.someMethod("some arg");
//Second call: prints "foo"
System.out.println(mock.someMethod("some arg"));
//Any consecutive call: prints "foo" as well (last stubbing wins).
System.out.println(mock.someMethod("some arg"));
Your example would then look similar to:
PreparedStatement stmt = null;
ResultSet mockResult1 = Mockito.mock(ResultSet.class);
Mockito.when(mockResult1 .next()).thenReturn(true);
Mockito.when(mockResult1 .getString(1)).thenReturn("foo");
Mockito.when(mockResult1 .getString(2)).thenReturn("bar");
Mockito.when(mockResult1 .getInt(3)).thenReturn(55);
ResultSet mockResult2 = Mockito.mock(ResultSet.class);
Mockito.when(mockResult2 .next()).thenReturn(true);
Mockito.when(mockResult2 .getString(1)).thenReturn("lorem");
Mockito.when(mockResult2 .getString(2)).thenReturn("ipsum");
Mockito.when(mockResult2 .getInt(3)).thenReturn(55);
Mockito.when(stmt.executeQuery()).thenReturn(mockResult1).thenReturn(mockResult2);

Related

Using jdbcTemplate nested in another jdbcTemplate (Before start of result set)

I make a query:
jdbcTemplate.query(sqlQueryForGetNodes, new Object[]{treeId, versionId}, rs -> {
NodeType nodeType = NodeType.get(rs.getInt("NodeTypeId"));
int nodeId = rs.getInt("NodeId");
SmbpTreeSwitchCase switchCase = new SmbpTreeSwitchCase();
switchCase.setSwitchConditionType(getSwitchTypeByNodeId(nodeId));
smbpTreeNodes.add(switchCase);
});
private SwitchConditionType getSwitchTypeByNodeId(int nodeId) {
String sqlQueryForGetSwitchTypeByNodeId = "SELECT SwitchConditionTypeId FROM RE.SwitchCondition sc WHERE sc.NodeId = ?";
return jdbcTemplate.query(sqlQueryForGetSwitchTypeByNodeId, resultSet -> {
return SwitchConditionType.get(resultSet.getInt("SwitchConditionTypeId"));
}, nodeId);
}
And in this query, I need to subquery another table in method getSwitchTypeByNodeId to fill the object with data.
But I'm getting an error:
org.springframework.dao.TransientDataAccessResourceException: PreparedStatementCallback; SQL [SELECT SwitchConditionTypeId FROM RE.SwitchCondition sc WHERE sc.NodeId = ?]; Before start of result set; nested exception is java.sql.SQLException: Before start of result set
What am I doing wrong? Tell me how to correctly make a subquery in jdbcTemplate?
I think your two lambdas in your two calls to JdbcTemplate.query have different types and hence are being treated differently by Spring.
The first call, which appears to work, appears to use the overload of the JdbcTemplate.query method that takes a String, an Object[] and a RowCallbackHandler and returns void. In this case, your lambda is a RowCallbackHandler which is called once for each row in the result-set. Spring will be advancing the result-set from one row to the next; it's just up to you to do something with each row.
The second call, however, appears to use the overload of JdbcTemplate.query that takes a String, a ResultSetExtractor and a varargs array of arguments, and returns whatever the ResultSetExtractor returns. When using a ResultSetExtractor, Spring will call you only once and expect you to do all of the handling of the result-set. In particular, you will need to check whether the result-set contains any data at all.
Try modifying your second lambda to something like the following:
return jdbcTemplate.query(sqlQueryForGetSwitchTypeByNodeId, resultSet -> {
if (resultSet.next()) {
return SwitchConditionType.get(resultSet.getInt("SwitchConditionTypeId"));
} else {
// SQL query returned no rows. TODO handle this somehow...
}
});

how to merge two different resultsets from two different DBs in java

I have two resultsets, one from DB2 and one from Sybase.
I want to merge these resultsets based on some condition,for which i have made one function which will take two resultsets and merge them.
But i am getting SQL exception- Resultset Closed
I am using Connection object and Prepared statement to connect to the respective DB and execute the query.
public void ExecuteDB2Query(SQLQuery){
Connection DB2con = DB2Sess.connection();
PreparedStatement statement = DB2con.prepareStatement(SQLQuery);
MyResulset1= statement.executeQuery();
}
Another method:
public void ExecuteSybaseQuery(SQLQuery){
Connection Sybasecon = SybaseSess.connection();
PreparedStatement statement = Sybasecon.prepareStatement(SQLQuery);
MyResulset2= statement.executeQuery();
}
Final merge method
puble void merge{
while(MyResultset1.next()){
while(MyResultset2.next()){
<some code here>
}
Do we have any way by which i can loop through these two result sets? without any exception.
It looks like you are trying to loop through those two result sets with a nested loop. That can't be done, since after the inner loop finished its first iteration, MyResultset2 cannot be used anymore.
I suggest you iterate over the two result sets separately and store their data in some Collections. Then you can iterate over those Collections however you like.
Ok, i'll give you your code check it.
public void ExecuteDB2Query(SQLQuery){
Connection DB2con = DB2Sess.connection();
PreparedStatement statement = DB2con.prepareStatement(SQLQuery,ResultSet.TYPE_SCROLL_INSENSITIVE);
myResulset1 = statement.executeQuery();
}
public void ExecuteSybaseQuery(SQLQuery){
Connection Sybasecon = SybaseSess.connection();
PreparedStatement statement = Sybasecon.prepareStatement(SQLQuery,ResultSet.TYPE_SCROLL_INSENSITIVE);
myResulset2 = statement.executeQuery();
}
public void merge{
while(myResultset1.next()){
myResultset2.first();
while(myResultset2.next()){
<some code here>
}
}
}
make changes in this code according to your requirement.

JMockit | trying to define different return values based on parameters but getting unexpected results

I've got a class like the following:
class A
{
public method doSomething()
{
//....
DAO dataAccessor = new DAO();
List<Object> result1 = dataAccessor.getData(dataAccessor.getSql1());
List<Object> result2 = dataAccessor.getData(dataAccessor.getSql2());
//.. do some stuff with the results
}
Now, I use jMockit for testing the above function, by mocking the DAO class.
This is how my test class looks like:
class A_Test
{
private A myAClass;
#Mocked DAO mockedDAO;
List<Object> resultToSql1, resultToSql2;
// ... Some initializations...
#Test
public void testDoSomething()
{
new NonStrictExpectations()
{
mockedDAO.getSql1(); result = "SQL1";
mockedDAO.getData(withEqual("SQL1")); result = resultToSql1;
mockedDAO.getSql2(); result = "SQL2";
mockedDAO.getData(withEqual("SQL2")); result = resultToSql2;
};
myAClass.doSomething();
}
}
Now, it seems that the second expectation regarding getData() masks the first one, i.e. the mock object behaves as if I never declared the first lines in the expectation (the ones that handle sql1):
The first call to getData() returns empty collection, instead of the values with which I initialized resultToSql1. The second call returns resultToSql2, as expected.
If I comment the following line:
mockedDAO.getData(withEqual("SQL2")); result = resultToSql2;
the first call is returning what I defined - resultToSql1, and the second returns empty collection.
This makes sense.
So, what am I doing wrong? ?How can I define two different return values from getData() based on the parameters of the call?
Any help would be appreciated.
Thanks!
So, After digging more deeply inside the manual, I found that:
...But what if a test needs to decide the result of a recorded invocation based on the arguments it will receive at replay time? We can do it through a mockit.Delegate instance ...
So, in order to solve the above problem, the expectations block should look like this:
new NonStrictExpectations()
{
mockedDAO.getSql1(); result = "SQL1";
mockedDAO.getSql2(); result = "SQL2";
mockedDAO.getData(anyString);
result = new mockit.Delegate()
{
List<Object> getData(String sql)
{
if (sql.equals("SQL1"))
return resultToSql1;
if (sql.equals("SQL2"))
return resultToSql2;
return null;
}
}
};

test case issue

hi
I have one class,and in that class I have one method as follows:
I am using java.
public class ABC{
some code here...
..................
PreparedStatement pre;
public void insertUser(String firstName,String lastName){
pre.setString(1,firstName);
pre.setString(2,lastName);
pre.execute();
}
}
for above method I want to write the test case,I have created the test class as follows:
public class ABCTest{
ABC abc=new ABC();
public void testInsertUser(){
now here I want to write the assert statement,I want test whether insert is successful,
I want to test the method variable using assert(). Please tell me how to write test case for this in java.
You need to do something like these:
Check the count of rows before and after the INSERT and assert that the after count is one greater than the before count.
Do a SELECT and see if your row made it into the database.
Check the number of rows affected that is returned by the call and see that it equals one.
probably you have along with your 'insertUser' method other methods like 'selectUser' and 'removeUser' so in your testCase you should use 'selectUser' method to be sure that your user appear in DB, also in the end you should call 'removeUser' cause after the tests you should keep your data clean as it was before (for tests to be able to run multiple times on same test data with same results).
do you mean something like:
public void testInsertUser(){
ABC.insertUser(User.firstname, user.lastname);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT firstname_Colname, lastname_Colname FROM Your_Table");
boolean found = false;
while (rs.next())
{
found = ((User.firstname = rs.getString("firstname_Colname")) and (User.lastname = rs.getString("lastname_Colname")));
if (found)
{
break;
}
}
assertEquals('test failed!', true, found);
}
if yes you are actually testing the PreparedStatement which doesnt make any sense for me!

Best practices: working with DB in Java

First of all, I'm new to Java.
I'm trying to figure out what would be a good/handy way to work with DB from Java. I'm using c3p0 for connection pooling. Hibernate or other ORM is not an option this time, we decided to stick with "plain SQL" for now.
Currently basic retrieval of data looks like this:
private int getUserID(int sessionID, String userIP) {
int result = 0;
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try {
// Application.cpds is an instance of c3p0's ComboPooledDataSource
conn = Application.cpds.getConnection();
st = conn.prepareStatement("SELECT user_id, user_ip, is_timed_out FROM g_user.user_session WHERE id = ?");
st.setInt(1, sessionID);
rs = st.executeQuery();
if ( rs.next() ) {
if ( !rs.getBoolean("is_timed_out") && userIP.equals(rs.getString("user_ip")) ) {
result = rs.getInt("user_id");
}
}
}
catch (SQLException e) {
e.printStackTrace();
}
finally {
if ( rs != null ) {
try { rs.close(); } catch (SQLException e) { e.printStackTrace(); }
}
if ( st != null ) {
try { st.close(); } catch (SQLException e) { e.printStackTrace(); }
}
if ( conn != null ) {
try { conn.close(); } catch (SQLException e) { e.printStackTrace(); }
}
}
return result;
}
The code looks very long for such a basic operation. Another problem is that most of the code would have to be repeated in many places (declaring Connection, PreparedStatement, ResultSet, closing them, catching exceptions). Though, this is what I see in most examples when googling.
In PHP I would create a wrapper class that would have method select() that accepts 2 arguments (string)sqlQuery and (array)parameters and would return simple array of data. Wrapper class would also have few more specific methods, like:
selectValue() for single value (e.g., select count(*) from user)
selectRow() for single row (e.g., select name, surname from user where id = :user_id)
selectColumn for single column (e.g., select distinct remote_address from user)
Is anything like this practiced in Java? Or is there anything better / handier? Or should I use same style as in getUserID() example above? As I said, ORM is not an option this time.
Thanks in advance :)
edit: Currently DBConnection class is written. It gets connection from c3p0 connection pool in constructor. It has few public methods for working with DB: select() for tabular data, selectValue() for single value, selectRow() and selectColumn() for single row or column, as well as insert(), update(), delete() and ddl(). Methods accept String query, Object[] params arguments, with params being optional. insert(), update() and delete() return Integer which is result of PreparedStatement.executeUpdate(). select methods return different results:
ArrayCollection<HashMap<String, Object>> select()
Object selectValue()
HashMap<String, Object> selectRow()
ArrayCollection<Object> selectColumn()
The last problem is with compiler warnings - "warning: [unchecked] unchecked cast". This is because all methods call single private method that returns Object and cast its result to mentioned types. As I am new to Java, I'm also not sure if I have chosen appropriate types for selects. Other than that, everything seems to work as expected.
If the an ORM is no option, you could still use Spring's JDBC helper classes:
http://docs.spring.io/spring-framework/docs/4.1.0.RELEASE/spring-framework-reference/html/jdbc.html
Or you could simply write some helper methods on your own. Maybe a DBUtil.close(conn, st, rs); would be nice.
And by the way, you really should use a logging framework instead of "e.printStackTrace()"
EDIT:
One more thing: I think it's kind of hard to add ORM afterwards, when you have all the SQL already written in plain JDBC. You can't refactor that stuff, you have to throw it away and do it again.
EDIT:
You don't have to close the resultSet if you are closing the statement anyway. The Java ResultSet API reads:
A ResultSet object is automatically
closed when the Statement object that
generated it is closed, re-executed,
or used to retrieve the next result
from a sequence of multiple results.
Beside that, C3P0 does resource management as well and and closes Statements when you return a connection. You might to look that up too.
To avoid the repetition of code and perhaps makes things simpler.
What you could do is create a Database class.
In the class you could then create general purpose methods for access to the database.
For eg.
if the class is called DBManager.java then inside
create methods
private connect()
public boolean update()
public ResultSet query()
Reason for connect method is obvious, you use it get your connection. Since its private you call it in the constructor of DBManager.
You then use your update() method to allow you to perform SQL inserts,update,delete and the like, basically any SQL operation that doesn't return any data except for maybe a status of its success is done with the update method.
Your query method is used when you want to do a select query. You can thn return the resultset and then iterate through the results in the calling method/class
How you handle exceptions is up to you. It may be nicer on you to handle exceptions in the DBManager class that way you won't have to handle them in the various classes that you make a query from.
So instead of
public ResultSet query() Throws SQLException{
you would use a try catch inside the query method like you did in your examples above.
The obvious advantage of handling it in the dbmanager class is that you won't have to worry about it in all the other classes that make use of your sql connection.
Hope that's helpful
in response to your comment:
Its up to you what you return, the ResultSet being return is only an idea but maybe it'd be best to return a collection of some sort instead of an array, maybe? depending on what you need. The resultset needn't be closed.
public ResultSet query(String strSql) {
try {
Statement tmpStatement = connection.createStatement();
ResultSet resultSet = tmpStatement.executeQuery(strSql);
return resultSet;
} catch (java.sql.SQLException ex) {
//handle exception here
return null;
}
}
your update can then look like so
public boolean updateSql(String strSQL) {
try {
Statement tmpStatement = connection.createStatement();
tmpStatement.executeUpdate(strSQL);
return true;
} catch (java.sql.SQLException ex) {
//handle exception
return false;
}
}
erm, you can then use your query method like so
ResultSet r = query(sql);
try {
while (r.next()) {
someVar[i] = r.getString("columnName");
}
} catch (SomeException ex) {
//handle exception etc
}
But then again as you said instead of returning a result set you could change the query method to copy your results to an array or collection and then return the collection and close the statement with
tmpStatement.close();
But when a Statement object is closed, its current ResultSet object, if one exists, is also closed.(from api docs)
Its good practice to free up database resources as soon as so copying your result to a collection object and then closing your statement is probably best. Again its up to you.
" Hibernate or other ORM is not an option this time, we decided to stick with "plain SQL" for now."
Out of curiosity, what was the reason to stick with plain SQL? Looking at the example and question you mentioned first obvious answer would be use ORM and don't bother - in most cases standard ORM feature list would be sufficient.
Obviously there are plenty of reasons not to use ORM's, so I'm interested in yours?
I think the level o granularity is always a developer decision. I mean, the great thing of having so many exceptions and validations is that you can capture the specific error and act according to it, however if what you need doesn't require that level of robustness and as you are showing it is just to print out the stack trace, I think a wrapper method can be useful in your case.

Categories

Resources