Junit Mockito test everything - java

I am searching for more hours now with no result. Please help...
This is my class to test:
public class DBSelectSchema extends Database {
private static final Logger LOG = Logger
.getLogger(DBSelectSchema.class.getName());
private Connection conn = null;
public DBSelectSchema() {
super();
}
/**
* This method will return the version of the database.
*
* #return version
* #throws Exception
*/
public JSONObject getVersionFromDB() throws SQLException {
ResultSet rs = null;
JSONObject version = new JSONObject();
PreparedStatement query = null;
try {
conn = mensaDB();
query = conn.prepareStatement("SELECT number FROM version");
rs = query.executeQuery();
if (rs.isBeforeFirst()) {
rs.next();
version.put(HTTP.HTTP, HTTP.OK);
version.put("version", rs.getString("number"));
} else {
version.put(HTTP.HTTP, HTTP.NO_CONTENT);
version.put(HTTP.ERROR, "Die SQL Abfrage lieferte kein Result!");
}
rs.close();
query.close();
conn.close();
} catch (SQLException sqlError) {
String message = ERROR.SQL_EXCEPTION;
LOG.log(Level.SEVERE, message, sqlError);
return version;
} catch (JSONException jsonError) {
String message = ERROR.JSON_EXCEPTION;
LOG.log(Level.SEVERE, message, jsonError);
return version;
}
return version;
}
I am trying to get in each branch for 100% code coverage.
How can I mock ResultSet rs, JSONObject version and PreparedStatement query to do/return what I want:
Currently I am testing like that:
#Test
public void getVersionFromDB_RS_FALSE() throws SQLException, JSONException {
MockitoAnnotations.initMocks(this);
Mockito.when(dbSelMocked.mensaDB()).thenReturn(conn);
Mockito.when(conn.prepareStatement(Mockito.anyString())).thenReturn(query);
Mockito.when(query.executeQuery()).thenReturn(rs);
Mockito.when(rs.isBeforeFirst()).thenReturn(false);
JSONObject returnObj = dbSelMocked.getVersionFromDB();
assert(...);
}
But this just works when the 3 variables are class variables (like Connection conn) and not local variables. But I dont want them (even Connection) not to be global.
=== EDIT 1 ===
It works like that if all variables are local:
#Test
public void getVersionFromDB_RS_FALSE() throws SQLException, JSONException {
System.out.println("####################");
System.out.println("started test: getVersionFromDB_RS_FALSE");
System.out.println("####################");
Connection conn = Mockito.mock(Connection.class);
PreparedStatement query = Mockito.mock(PreparedStatement.class);
ResultSet rs = Mockito.mock(ResultSet.class);
MockitoAnnotations.initMocks(this);
Mockito.when(dbSelMocked.mensaDB()).thenReturn(conn);
Mockito.when(conn.prepareStatement(Mockito.anyString())).thenReturn(query);
Mockito.when(query.executeQuery()).thenReturn(rs);
Mockito.when(rs.isBeforeFirst()).thenReturn(false);
JSONObject returnObj = dbSelMocked.getVersionFromDB();
assertTrue(returnObj.has("error"));
}
But I am not able to mock JSONObject version in another test anymore :(
How can I do that?
#Test
public void getVersionFromDB_JSON_EXCEPTION() throws SQLException, JSONException {
System.out.println("####################");
System.out.println("started test: getVersionFromDB_JSON_EXCEPTION");
System.out.println("####################");
JSONObject version = Mockito.mock(JSONObject.class);
MockitoAnnotations.initMocks(this);
doThrow(new JSONException("DBSelectSchemaIT THROWS JSONException")).when(version).put(anyString(), any());
JSONObject returnObj = dbSelMocked.getVersionFromDB();
System.out.println(returnObj.toString());
assertTrue(returnObj.equals(null));
}
I think its overwritten in the real method... because it does not throw an exception and the method does not fail.

Your test code has multiple issues.
The test is verbose and fragile
The same (verbose) setup is required for multiple tests
You don't test real object, instead you are using mock of your class for testing
The first 2 issues can be solved by extracting repeated code to a setup method (I added static import for Mockito to reduce the noise):
#Before
public void setUp() throws Exception {
Connection conn = mock(Connection.class);
PreparedStatement query = mock(PreparedStatement.class);
when(dbSelMocked.mensaDB()).thenReturn(conn);
when(conn.prepareStatement(anyString())).thenReturn(query);
when(query.executeQuery()).thenReturn(rs);
rs = mock(ResultSet.class); // rs is field
}
Now in each of your tests you can configure rs to return whatever you need:
#Test
public void getVersionFromDB_RS_FALSE() throws Exception {
// Given
when(rs.isBeforeFirst()).thenReturn(false);
// When
JSONObject returnObj = dbSelMocked.getVersionFromDB();
// Then
assertTrue(returnObj.has("error"));
}
Now the most important issue: you are mocking class DBSelectSchema to return connection mock. Mocking class under test can cause different hard-to-spot problems.
To solve this issue you have 3 options:
Refactor your code and inject some connection factory. So you'll be
able to mock it in your test.
Extend class DBSelectSchema in your test and override method
mensaDB() so it will return mocked connection
Use embedded database like H2 and put test data in 'number' table
before calling getVersionFromDB()
Option #1
Extract creation of connection to a separate class and use it in your DBSelectSchema:
public class ConnectionFactory {
public Connection getConnection() {
// here goes implementation of mensaDB()
}
}
Then inject it to your DBSelectSchema:
public DBSelectSchema(ConnectionFactory connFactory) {
this.connFactory = connFactory;
}
Now your test you can use real DBSelectSchema class with mocked ConnectionFactory
ConnectionFactory connFactory = mock(ConnectionFactory.class);
dbSel = new DBSelectSchema(connFactory);
Option #2
You can make almost real class under test:
final Connection conn = mock(Connection.class);
dbSel = new DBSelectSchema() {
#Override
public Connection mensaDB() {
return conn;
}
};
Option #3
This option is most preferable, because you will call real SQL commands and you mock the whole database instead of classes. It requires some effort to use plain JDBC here, but it worth that. Keep in mind that SQL dialect can differ from the DB used in production.
#Before
public void setUp() throws Exception {
Class.forName("org.h2.Driver");
conn = DriverManager.getConnection("jdbc:h2:mem:test;INIT=RUNSCRIPT FROM 'classpath:schema.sql'");
}
#After
public void tearDown() throws Exception {
conn.close();
}
Then in your test you simply add required records to DB:
#Test
public void getVersionFromDB() throws Exception {
// Given
conn.prepareStatement("INSERT INTO version(number) VALUES (1)").execute();
// When
JSONObject returnObj = dbSel.getVersionFromDB();
// Then
assert(...);
}
Obviously, DBSelectSchema must use the same connection, so you can use in combination with options #1 and #2,

You are unit testing data access layer by mocking all ADO calls. By doing so, you will end up with a unit test that does not really test any logic.
Taking an example from your code: assume that you are using the following sql to retrieve a version number : SELECT number FROM version. Now assume that the column name changed and you should retrieve 2 additional column from your sql. You will eventually end up with an sql like SELECT number, newColumn1, newColumn2 FROM version. With the test you would have written (using mock), it would still pass even though its not really testing whether the 2 new column is being retrieved. You get my point?
I would advise you to have a look at this thread for some possible alternative to test your data access layer. Using mock for your data access layer will end up with brittle test that does not really test anything

Your test is much too large, and you seem to be testing too much.
Split your code along it's natural breaks, so that the code that does the data retrieval is separate from the logic that manipulates it.
You only want to test the code that you write, not the 3rd party code. Its out of scope for your needs, and if you can't trust it, then don't use it.

Related

Jooq Converter Cast Exceptions

I'm currently trying to store encrypted data in some of the columns of a Postgres DB. After receiving helpful feedback from this question: client-side-encryption-with-java-and-postgres-database I am using converters/bindings to implement transparent encryption in the JDBC layer.
Right now I'm trying to insert a BigDecimal[][][] into a Postgres DB column of type bytea.
The insertion works but the problem is that the encryption code I've added in the converters/binding doesn't seem to run. Unfortunately, when I check the database I'm seeing an unencrypted 3D matrix. (FYI my encryption utility code is tested and does work)
To test, I put my encryption code in the DAO layer and the BigDecimal[][][] matrix does get encrypted on DB inserts. Although I could do this it defeats the purpose of using converters/bindings for encryption.
So my question:
With the code I provided below am I doing anything wrong that is preventing the encryption code in my converter/binding to be run? I thought after a Prepared Statement is executed the converter is the next step but maybe not? I have a lack of knowledge on just when the converter/binding code gets called in the whole JOOQ flow so any insight is much appreciated! Thanks :D
First I'm using a PreparedStatment in a DAO to execute the insert query.
I can't show the full code but basically for the stmt I'm setting the BigDecimal[][][] as an object parameter:
private Result executeInsert(BigDecimal[][][] valueToEncrypt, String insertSql) {
try (Connection conn = config.connectionProvider().acquire();
PreparedStatement stmt = conn.prepareStatement(insertSql)) {
// Get a human readable version of the 3d matrix to insert into the db.
PostgresReadableArray humanReadableMatrix = getPostgresReadableArray(valueToEncrypt)
stmt.setObject(parameterIndex++, humanReadableMatrix, Types.OTHER);
ResultSet res = stmt.executeQuery();
}
...
}
I am currently attaching the binding to a codegen xml file here:
<forcedType>
<userType>
java.math.BigDecimal[][][]
</userType>
<binding>com.myapp.EncryptionBinding</binding>
<includeExpression>matrix_column</includeExpression>
<includeTypes>bytea</includeTypes>
</forcedType>
Here is my binding class EncryptionBinding:
public class EncryptionBinding implements Binding<byte[], BigDecimal[][][]> {
#Override
public Converter<byte[], BigDecimal[][][]> converter() {
return new MatrixConverter();
}
// Rending a bind variable for the binding context's value and casting it to the json type
#Override
public void sql(BindingSQLContext<BigDecimal[][][]> ctx) throws SQLException {
}
// Registering VARCHAR types for JDBC CallableStatement OUT parameters
#Override
public void register(BindingRegisterContext<BigDecimal[][][]> ctx) throws SQLException {
ctx.statement().registerOutParameter(ctx.index(), Types.VARCHAR);
}
// Converting the BigDecimal[][][] to a Encrypted value and setting that on a JDBC PreparedStatement
#Override
public void set(BindingSetStatementContext<BigDecimal[][][]> ctx) throws SQLException {
ctx.statement().setBytes(ctx.index(), ctx.convert(converter()).value());
}
...
Here is my converter class MatrixConverter used in the above EncryptionBinding class:
public class MatrixConverter extends AbstractConverter<byte[], BigDecimal[][][]> {
private static final Logger logger = LoggerFactory.getLogger(MatrixConverter.class);
public MatrixConverter() {
super(byte[].class, BigDecimal[][][].class);
}
#Override
public BigDecimal[][][] from(byte[] databaseObject) {
return EncryptionUtils.decrypt(databaseObject);
}
#Override
public byte[] to(BigDecimal[][][] userObject) {
return EncryptionUtils.encrypt(JsonUtils.toJson(userObject));
}
}

Unit tests for one main method

I have a method. What is the best practice to unit testing?
You can make a mock Connection and ResultSet and pass as a method parameter as an object but I find it stupid, unprofessional solution.
public static void playA() {
MyObject object = check("cat");
// some stuff...
}
private static MyObject check(String s) throws SQLException {
// some stuff checking string
MyObject o = methodA(s);
// some stuff doing on the object
return o;
}
private static MyObject methodA(String s) throws SQLException {
Connection c = MySettings.getConnection();
PreparedStatement ps = c.prepareStatement("select * from Objects where type = ?");
ps.setString(1, s);
ResultSet resultSet = ps.executeQuery();
while (resultSet.next()) {
// mapping
return object;
}
return null;
}
My take is that it should be according the level of abstraction you want to test. Mocking Connection and ResultSet can be fine if you want to check the the logic of your SQL for example.
If you want to be precise testing the database connection layer you can use tools for unit testing database like http://dbunit.sourceforge.net/
Also try to have your code as most Object Oriented as possible. that would help you in testing ( although I know it's probably just an example )

Mocking JDBC's Connection#prepareStatement always returns null with jMockit

I am trying to test JDBC calls to Oracle DB with jMockit. I have tried to simulate JDBC's Connection#prepareStatement(String sql) to return PreparedStatement mock object, but I only get a null value instead. My aim is to mock Connection APIs to return a mock PreparedStatement object, and to mock PreparedStatement APIs to return a mock ResultSet object.
My source code is given below.
try (Connection conn = DriverManager.getConnection(url, username, password);) {
try(PreparedStatement ps = conn.prepareStatement(
"SELECT firstName, lastName from Employee where empId = ?");) {
ps.setString(1, empId); // This is an input to function.
try(ResultSet rs = ps.executeQuery();) {
while(rs.next()) {
Employee emp = new Employee();
emp.setFirstName(rs.getString("firstName"));
emp.setLastName(rs.getString("lastName"));
return emp;
}
}
}
}
return employees;
When I invoke
PreparedStatement ps = conn.prepareStatement(
"SELECT firstName, lastName from Employee where empId = ?")
My unit test is as follows
#Test()
public void testQueryOutOfDateSpanishContent_Success(
#Mocked final Connection connection, #Mocked final PreparedStatement ps) throws Exception {
new Expectations(DriverManager.class) {
{
DriverManager.getConnection(
dcrParameters.getEnvironmentUrl(), dcrParameters.getUsername(),
dcrParameters.getPassword());
result = connection;
connection.prepareStatement(anyString);
result = with(new Delegate<PreparedStatement>() {
public PreparedStatement prepareStatement(String sql) throws SQLException {
return ps;
}
});
}
};
// Call the source function here.
I am using TestNG 6.10 with latest version of jMockit release. I am running the unit test with TestNG eclipse plugin. I am passing -javaagent:C:\downloads\jmockit.jaras a VM argument in Eclipse.
Update: The method accepts two mocked arguments that are provided by jMockit. If I don't pass the java agent, TestNG throws an error expecting the arguments to be passed through TestNG's dataProvider functionality.
http://testng.org/doc/documentation-main.html#parameters-dataproviders
It does return a mock object, but it resorts to default behaviour. For example, I want to capture what value is being passed to ps.setString(1, empId) or to simulate rs.next() to return true. But only default outputs are returned.
It's not a bug. It's a feature :)
You need to configure the mock. By default they will return null.
You need to add 'Expectation'-s, as you can see on http://jmockit.org/tutorial/Mocking.html

Database Connectivity in java

First i show you the code then asked few questions. i have a class database connectivity like this (please ignore syntax error if any)
class DatabaseConnection {
private static Connection connection = null;
private static String driverName="";
private static String userName="";
private static String passwrod="";
private static String url="";
private DatabaseConnection() { }
public static void createConnection() {
if ( connection == null ) {
// read database credentials from xml file and set values of driverName, userName, passowrd and url
//create connection with database and set store this connection in connection object created a class level.
}
}
public static void closeConnection1() throws Exception{
if ( connection != null ) {
connection.close();
connection == null
}
}
public static void closeConnection2() throws Exception{
if ( connection != null ) {
connection.close();
}
}
public void insertData(Object data) {
// insetData in database
}
}
I want to know which close connection is more optimize in database connection. Lets suppose I have test class like this
class Test {
public static void main(String args[]) {
DatabaseConnection.createConnection();
DatabaseConnection.insertData(data);
DatabaseConnection.closeConnection2(); // we call also called close connection method within the insertData method after inserting the data
}
}
After creating database connection i insert data in database and then close the connection using closeConnection2 method. in this way connection has been close so if i want to insert some more method then i have to recreate connection with the database but i can't do this because connection object is not null and createConnection didn't execute the code inside the if statement. Now if I called closeConnection1 method for closing connection then in doing this i have to parse xml file again for credential which is not a optimize solution. can you tell me which method is good and if both are worse then please tell me more efficient way for creating and closing database connection.
I see two major problems with this:
The fact that everything (including the Connection object) is static means that you can't ever use this class from more than one thread at once.
parsing the configuration data and opening the connection are separate concerns and should not be mixed. At least move them into separate methods, the configuration could probably even go in another class.
The second thing alone will avoid having to parse the connection information multiple times.
An even better approach would be to use a DataSource instead of opening the connections each time. And then use a DataSource that's actually a connection pool!

Java: Prepare a statement without a connection

I'm trying to generate some sql files in my java application.
The application will not execute any sql statements, just generate a file with sql statements and save it.
I'd like to use the java.sql.PreparedStatement to create my statements so that i don't have to validate every string etc. with my own methods.
Is there a way to use the PreparedStatement without the calling java.sql.Connection.prepareStatement(String) function, because I don't have a java.sql.Connection?
Take a look at this Java library: http://openhms.sourceforge.net/sqlbuilder/
I'm guessing that until you've got a sql connection, the parser won't know what rules to apply. I'm guessing that it's actually the SQL driver or even server that's compiling the sql statement.
Assuming your sql is simple enough, then how about using a cheap connection, like, say a sqlite connection.
SQLite will create a new database on the fly if the database you're attempting to connect to does not exist.
public Connection connectToDatabase() {
// connect to the database (creates new if not found)
try {
Class.forName("org.sqlite.JDBC");
conn = DriverManager.getConnection("jdbc:sqlite:mydatabase.db");
// initialise the tables if necessary
this.createDatabase(conn);
}
catch (java.lang.ClassNotFoundException e) {
System.out.println(e.getMessage());
}
catch (java.sql.SQLException e) {
System.out.println(e.getMessage());
}
return conn;
}
Not really. Preparing a statement in most cases means that it will be compiled by DBMS which is "hard" without connection.
http://java.sun.com/docs/books/tutorial/jdbc/basics/prepared.html
This is a dastardly devious problem, thankfully it's pretty easy to cope with:
public class PreparedStatementBuilder
{
private String sql; // the sql to be executed
public PreparedStatementBuilder(final String sql) { this.sql = sql; }
protected void preparePrepared(final PreparedStatement preparedStatement)
throws SQLException
{
// this virtual method lets us declare how, when we do generate our
// PreparedStatement, we want it to be setup.
// note that at the time this method is overridden, the
// PreparedStatement has not yet been created.
}
public PreparedStatement build(final Connection conn)
throws SQLException
{
// fetch the PreparedStatement
final PreparedStatement returnable = conn.prepareStatement(sql);
// perform our setup directives
preparePrepared(returnable);
return returnable;
}
}
To use, just write an anonymous class that overrides void preparePrepared(PreparedStatement):
final String sql = "SELECT * FROM FOO WHERE USER = ?";
PreparedStatementBuilder psBuilder = new PreparedStatementBuilder(sql){
#Override
protected void preparePrepared(PreparedStatement preparedStatement)
throws SQLException
{
preparedStatement.setString(1, "randal");
}};
return obtainResultSet(psBuilder);
Presto! You now have a way to work with a PreparedStatement without yet having built it. Here's an example showing the minimal boilerplate you'd otherwise have to copy paste to kingdom come, every time you wanted to write a different statement:
public ResultSet obtainResultSet(final PreparedStatementBuilder builder)
throws SQLException {
final Connection conn = this.connectionSource.getConnection();
try
{
// your "virtual" preparePrepared is called here, doing the work
// you've laid out for your PreparedStatement now that it's time
// to actually build it.
return builder.build(conn).executeQuery();
}
finally
{
try { conn.close(); }
catch (SQLException e) { log.error("f7u12!", e); }
}
}
You really really don't want to be copy pasting that everywhere, do you?
Try implementing PreparedStatement.
Example : class YourOwnClass implements PreparedStatement {
// 1. Do implement all the methods ,
2. Get the minimal logic to implement from OraclePreparedStatement(classes12.jar) or
sun.jdbc.odbc.JdbcOdbcCallableStatement
}

Categories