Java, Spring: Testing DAOs for DataAccessException with Mockito - java

I'm trying to increase my test coverage so I'm wondering, how would you go about testing for DataAccessExceptions being thrown in a DAO, for example in a simple findAll method which just returns all the data from your data source? In my case, I'm using Spring JdbcTemplates.
For general testing I have my setUp-method with an #Before annotation, mocking the jdbcTemplate used, setting it up in the DAO and mocking all jdbc calls. Now forcing a DataAccessException for something like a create method is pretty simple, just throw the exception when calling a create statement with the right primary keys.
However, I really have no idea how to handle this for methods like simple findAll methods which don't take any input parameters. Testing the valid implementation is straight forward, but how would you go about mocking having no DB connection without it affecting every other test or method?
This would be a concrete implementation of a method I'd like to test:
public List<SomeObject> findAll() throws PersistenceException {
final String sql = "SELECT * FROM SomeObject";
try {
return jdbcTemplate.query(sql, new JdbcSomeObjectMapper());
} catch (DataAccessException ex) {
LOG.error(ex.getMessage());
throw new PersistenceException(ex.getMessage());
}
}
Which would just return all objects in the data source. Testing for a valid call is easy since I can just mock the jdbcTemplate.query call, but I'd never enter the catch block unless there's a connection failure while retrieving the data, and that's what I'd like to test.

Using Mockito you can mock a Class and the method calls of that particular Class. A mocked object can also be asked to throw an exception when particular methods are called on it. First you have to mock your jdbcTemplate, then stub your exception
//mocking JdbcTemplate
JdbcTemplate template = Mockito.mock(JdbcTemplate.class);
Mockito.when(template.query(Mockito.anyString(), (RowMapper<YourClass>) Mockito.any(RowMapper.class))).thenThrow(EmptyResultDataAccessException.class);
//or using EasyMock
EasyMock.expect(template.query(Mockito.anyString(), (RowMapper<YourClass>) Mockito.any(RowMapper.class))).andThrow(new (typeofExecption));

Related

Why #Transactional also work in aop #Before method?

I have ProductService class in my project for Product CRUD
#Transactional
public Product registerNewProduct(ProductPayload productPayload) {
Product product = productPayload.getProduct();
PriceHistory priceHistory = productPayload.getPriceHistory();
if(priceHistory == null)
throw new PriceInfoNotExistException("Price info is needed to register new product.");
And before executing method, I'm trying to insert API call log info to my database using AOP #Before.
#Before("execution(com.example.demo.entity.Product com.example.demo.service.ProductService.*(..))")
public void beforeBeginTransaction(JoinPoint joinPoint) {
//some code here
productChangeHistoryRepository.saveAndFlush(productChangeHistory);
}
I'm testing CREATE operation without providing price info so I can get PriceInfoNotExistException.
But If PriceInfoNotExistException thrown, this code below executed but no data inserted.
productChangeHistoryRepository.saveAndFlush(productChangeHistory);
I think it's because of #Transactional annotation on registerNewProduct() method Because If I remove this annotation and test again without price info, log data successfully saves to database.
Is there any way that I can save some data in AOP #Before method while keeping the #Transactional annotation on my registerNewProduct()?
Spring will create a proxy method (where the execution of your #Before aspect code will be at first place) with all annotations the initial method have.
Not sure if AOP is a good choice to test the method. Try to mock different ProductPayload objects (with or without priceHistory) and call your method on it.

mock jooq select request

I have my service with jooq select in myMethod()
Result<MyRecord> records = dsl.selectFrom(MyTable).fetch();
And I try to write unit-test using mockito. I want to mock my dsl and get 2 different result when I call service.myMethod()
Mockito.when(dsl.selectFrom(MyTable)).thenReturn(result)
But it doesn't work. How can I mock my select request?
Mocking the jOOQ API
If you want to mock the jOOQ API, the interesting methods for you to mock are the various fetch() methods, or execute() methods, as they act like the "terminal" methods of the jOOQ DSL API. All the intermediate methods should produce new mocks, not actual results.
However, as jOOQ's DSL is vast, you might be overlooking a variety of edge cases, so I think that mocking it entirely might not work too well.
Mocking the JDBC API
If you really want to mock the jOOQ API, I would rather mock it on the JDBC level. jOOQ implements a mock JDBC Connection, allowing to mock the entirety of JDBC using a single lambda expression, where you can do something like this (from the manual):
MockDataProvider provider = new MyProvider();
MockConnection connection = new MockConnection(provider);
DSLContext ctx = DSL.using(connection, SQLDialect.ORACLE);
Result<BookRecord> result = ctx.selectFrom(BOOK).where(BOOK.ID.eq(5)).fetch();
And then (simplified):
public class MyProvider implements MockDataProvider {
#Override
public MockResult[] execute(MockExecuteContext ctx) throws SQLException {
DSLContext ctx = DSL.using(SQLDialect.ORACLE);
MockResult[] mock = new MockResult[1];
String sql = ctx.sql();
if (sql.toUpperCase().startsWith("DROP")) {
throw new SQLException("Statement not supported: " + sql);
}
else if (sql.toUpperCase().startsWith("SELECT")) {
Result<Record2<Integer, String>> result =
ctx.newResult(AUTHOR.ID, AUTHOR.LAST_NAME);
result.add(cctx
.newRecord(AUTHOR.ID, AUTHOR.LAST_NAME)
.values(1, "Orwell"));
mock[0] = new MockResult(1, result);
}
return mock;
}
}
There's also an out of the box, file-based version of this mock implementation, the MockFileDatabase, which uses regular expressions to match SQL strings and provides a text based syntax to construct result sets for your queries.
Mocking databases in general
Please beware that mocking is not a good way to test database interaction - it only gets you so far, in simple cases. A much better, thorough, approach are integration tests. If you must, using an in-memory database, but ideally using your production database product (e.g. Oracle, PostgreSQL, etc.) via something like testcontainers.

how can I test a void method that insert a record in database using Junit?

I have a method that calls another method to retrieve data and insert it into the database, how can I test this method in Junit since this method retrieve nothing? Can anyone provide me some example of this situation?
public static void method(){
User user = getUser();
try {
String Query = "INSERT INTO users (USER_ID , Name) VALUES ("
+user.getID()+","+user.getName()+")";
Statement statement = conn.createStatement();
statement.executeUpdate(Query);
} catch (Exception e) {
e.printStackTrace();
}
}
I was thinking to use mock object for user, but I'm not sure how can I check if the user is inserted to the database. please help.
There are few pointers here but before that, you have to be sure about what's the code under test. The reason I say this is because that will actually make you refactor your code a lot.
For example, for the code below:
public static void method(){
User user = getUser();
try {
String query = "INSERT INTO users (USER_ID , Name) VALUES ("
+user.getID()+","+user.getName()+")";
Statement statement = conn.createStatement();
statement.executeUpdate(query);
} catch (Exception e) {
e.printStackTrace();
}
}
you might want to test following:-
Whether the query string is created properly i.e. a valid SQL as expected?
Should the method never throw an exception as we want to catch all the checked exception?
All the resources are closed once the work is done? e.g. in your method connection is opened but never closed.
Now out of above points, there could be some points with higher importance e.g. ensuring query is built correctly.
So, for that, the query builder code should be pulled out of this method and now you could easily test this query in the separate unit test.
Similarly, you would want to make sure a connection is closed after its job is completed. Hence, you might want to extract this code to a method that accepts a query as param, open connection, performs DB CRUD closes the connection and returns the output. And make sure that you are not repeating this code all the time in different methods.
Now, let's go to the pointers:-
If you at any point think that there is some code inside a method that's not testable till that code is part of the method. Please pull it out to a public / private method and test it.
Never try to test DB persistence as part of a unit test. The DB drivers etc are already having their own tests and are globally used.
If you think your method needs to be tested for whether it is called as many times as expected (it's what you want to test). It's called interaction based testing
You could use mocking (Mockito) and stub out the method under test and then assert how many times the method should be called. See this and this link.
Hope it helps!
Classes should be tested against the expected behavior.
Here testing the return type (even if it had other thing as void) makes no sense.
Your method performs a SQL query.
So your unit test has to assert the expected side effect on the database : an insertion with the expected values.
Note that to have reproducible and fast tests, you should favor embedded databases for unit testing.

Mock method with Consumer

I want to mock repository.actionOnFile(String path, Consumer<InputStream> action) in this source:
#Autowired
private FileRepositoryService repository;
public Document getDocument(URL url) {
MutableObject<Document> obj = new MutableObject<>();
Consumer<InputStream> actionOnFile = inputStream -> obj.setValue(getDocument(inputStream));
try {
repository.actionOnFile(url.toExternalForm(), actionOnFile);
} catch (S3FileRepositoryException e) {
throw e.getCause();
}
return obj.getValue();
}
The problem is that the second argument is a lambda expression.
How to mock it with mockito, I need to pass to the accept method the input stream to test it?
I found solution!
doAnswer(ans -> {
Consumer<InputStream> callback = ans.getArgument(1, Consumer.class);
InputStream stream = new ByteArrayInputStream("test".getBytes(StandardCharsets.UTF_8));
callback.accept(stream);
return null;
}).when(repository).actionOnFile(eq("any"), any(Consumer.class));
If you only want to mock the Function argument then the following would work:
Mockito.when(convertStringtoInt(Mockito.any(String.class), Mockito.any(Consumer.class))).then[...]
How to mock it with mockito, I need to pass in accept method test
input stream?
In your case, you want to test the getDocument() method.
So what you need to mock is the dependency of the class under test :
that is the repository field.
actionOnFile.add() more specifically should be mocked.
According to your code, either the method should throw S3FileRepositoryException or it provokes a side effect not visible in the code.
In the exception scenario, you should write something as :
 Mockito.when(fileRepositoryServiceMock.actionOnFile(url.toExternalForm(), actionOnFile)).thenThrow(new S3FileRepositoryException(cause));
And in the successfull, you should just verify that the method is invoked :
 Mockito.verify(fileRepositoryServiceMock).actionOnFile(url.toExternalForm(), actionOnFile));
Mocking a Consumer is really not a big deal.
It is a interface, you can mock any interface with Mockito.
The real issue is actually the Consumer makes not part of the API of the tested method.
It is a local variable.
Besides, it relies on an inputStream field that is not show in the code.
You cannot and have not to mock internal things.
Note that it also relies on a overloaded getDocument() method that is not mocked. So you would need to provide a consistent InputStream if you want to getDocument() that accepts a inputStream doesn't throw an exception.
Long story short : I think that you should either rethink your design to extract the depending processings in another class or write an integration test.

supressing NullPointerException in Mockito?

DAOClass.makeDBConnection() method returns datasource configured (using lookup) in Application Server (Jboss). Need to implement junit test case for this scenario.
Using Mockito, tested the DAO method as follows. As it's not able to find the datasource(as expected), it's returning NullPointerException. How to handle NullPointerException and return the connection which i am creating in below code? OR is there any other better unit test framework which handles this scenario ?
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
Connection conn = DriverManager.getConnection("jdbc:sqlserver://DB:1433;DatabaseName=databasename", "userid", "password");
when(DAOClass.makeDBConnection()).thenReturn(conn);
Mockito can't mock static method calls the way you have it; it effectively works by dynamically overriding all methods via a generated subclass (proxy).
You will need to write a wrapper class around static methods that you want to mock, otherwise refactor the code to avoid the static call, or use a tool such as PowerMock to rewrite your system's bytecode at runtime.

Categories

Resources