I have a JAVA rest service that interacts with the database , does some manipulation and returns the data.
I am trying to write test cases for these APIs.
I am trying to use the below link for reference to implement this.
http://www.developer.com/java/other/article.php/10936_3882311_2/Mockito-Java-Unit-Testing-with-Mock-Objects.htm
Here, calls made to the database are suppressed and the dto is mocked with made up values.
Is there an alternate method where we actually get to run the queries w/o talking to the db , (an in-memory db may be? )
Any code sample or reference would be of great help.
For a pure HashMap solution, something like this would work though then you would loose access to SQL query functionality (unless you mock the query too).
public class MockDatabase<T> {
protected Map<Serializable, T> fakeDatabase = Maps.newHashMap();
private final CustomRepository<T,Serializable> repository;
private Validator validator;
public void setValidator(Validator validator) {
this.validator = validator;
}
public static <T extends CustomRepository> T mock(Class<T> classToMock, Validator validator) {
T repository = Mockito.mock(classToMock);
MockDatabase md = new MockDatabase<T>(repository, validator);
return repository;
}
public <ID extends Serializable> MockDatabase(CustomRepository<T, ID> repository, Validator validator){
this.repository = (CustomRepository<T, Serializable>) repository;
this.validator = validator;
reset(repository);
doAnswer(new Answer<Object>() {
#Override
public Object answer(InvocationOnMock invocation) throws Throwable {
fakeDatabase.clear();
return null;
}
}).when(repository).deleteAll();
when(repository.save((T) anyObject())).thenAnswer(new Answer<T>() {
#Override
public T answer(InvocationOnMock invocation) throws Throwable {
return saveOrSaveAndFlush(invocation);
}
});
when(repository.getReference((ID)anyObject())).thenAnswer(new Answer<T>() {
#Override
public T answer(InvocationOnMock invocation) throws Throwable {
return fakeDatabase.get(invocation.getArguments()[0]);
}
});
when(repository.findOne((ID)anyObject())).thenAnswer(new Answer<T>() {
#Override
public T answer(InvocationOnMock invocation) throws Throwable {
return fakeDatabase.get(invocation.getArguments()[0]);
}
});
doAnswer(new Answer<T>() {
#Override
public T answer(InvocationOnMock invocation) throws Throwable {
return fakeDatabase.remove(ReflectionTestUtils.invokeGetterMethod(invocation.getArguments()[0], "getId"));
}
}).when(repository).delete((T)anyObject());
doAnswer(new Answer<ID>() {
#Override
public ID answer(InvocationOnMock invocation) throws Throwable {
fakeDatabase.remove(((ID)invocation.getArguments()[0]));
return null;
}
}).when(repository).delete((ID)anyObject());
when(repository.saveAndFlush((T) anyObject())).thenAnswer(new Answer<T>() {
#Override
public T answer(InvocationOnMock invocation) throws Throwable {
return saveOrSaveAndFlush(invocation);
}
});
when(repository.exists((ID)anyObject())).thenAnswer(new Answer<Boolean>() {
#Override
public Boolean answer(InvocationOnMock invocation) throws Throwable {
return fakeDatabase.containsKey(invocation.getArguments()[0]);
}
});
when(repository.merge(anyObject())).thenAnswer(new Answer<T>() {
#Override
public T answer(InvocationOnMock invocation) throws Throwable {
return (T) invocation.getArguments()[0];
}
});
when(repository.findAll()).thenAnswer(new Answer<List<T>>() {
#Override
public List<T> answer(InvocationOnMock invocation) throws Throwable {
return Lists.newLinkedList(fakeDatabase.values());
}
});
customMethods();
}
private T saveOrSaveAndFlush(InvocationOnMock invocation) throws NoSuchMethodException {
Object[] args = invocation.getArguments();
Serializable id = (Serializable) ReflectionTestUtils.getField(args[0], "id");
if (id == null) {
Class<?> returnType = args[0].getClass().getMethod("getId").getReturnType();
if (returnType.equals(Long.class)) {
id = (Long) new Random().nextLong();
} else if (returnType.equals(Integer.class)) {
id = (Integer) new Random().nextInt();
}
ReflectionTestUtils.setField(args[0], "id", id);
}
Set<ConstraintViolation<T>> validations = validator.validate((T)args[0]);
if (!validations.isEmpty()){
throw new IllegalStateException("Object failed validations (it would also fail on a db): "+validations);
}
for (Method method: args[0].getClass().getDeclaredMethods()){
if (method.isAnnotationPresent(Basic.class)){
Annotation a = method.getAnnotation(Basic.class);
if (!(boolean) AnnotationUtils.getValue(method.getAnnotation(Basic.class), "optional")){
if (ReflectionTestUtils.invokeGetterMethod(args[0], method.getName()) == null){
throw new IllegalStateException(args[0].getClass().getSimpleName()+"."+method.getName() + " returned null, but marked with #Basic(optional=false) - it would also fail on a db: "+validations);
}
}
}
}
fakeDatabase.put(id, (T) args[0]);
return (T) args[0];
}
public void customMethods() {
// override here if you want
}
}
If you had #Entity annotated POJOs, then with say hibernate library you can ask it to export to HSQLDB script and then use that. Eg you export via:
Configuration configuration = new Configuration();
try {
classes().forEach(cl -> {
configuration.addAnnotatedClass(cl);
});
configuration.setProperty("hibernate.dialect", HSQLCustomDialect.class.getName());
SchemaExport schemaExport = new SchemaExport(configuration);
schemaExport.setOutputFile("someFileName.sql");
schemaExport.setFormat(false);
schemaExport.setDelimiter(";");
schemaExport.execute(true, false, false, true);
and thereafter you would use spring to insert that SQL script for you:
#ActiveProfiles("test")
#SqlConfig(transactionMode = SqlConfig.TransactionMode.ISOLATED)
#SqlGroup({
#Sql(statements = "DROP SCHEMA PUBLIC CASCADE"),
#Sql(scripts = "classpath:yourGeneratedSQL.sql"),
})
public class DAOIntegrationTest {
HSQLDB is one in memory db i'm familiar with. The examples showed here against HSQLDB used with hibernate and JPA. http://uaihebert.com/tdd-with-hsqldb-jpa-and-hibernate/.
However i think it'll be useful to ask why you would prefer connecting to an in memory db than mocking the db in your situation?
It boils down to what scope of testing unit/integration you are trying to achieve.
What are you trying to test the manupilation logic in the rest layer? Where mocking is sufficient.
Are you trying to test how the rest handles the data access behavior, such as db error handling etc, than in memory might be slightly better.
Is the thing you are testing dependent on data setup/ testing data setup , in which case in memory db might be closer, since you can use the same/similar sql creation to test in inmemory db.
Related
I have a class which extends SequenceStyleGenerator for generating custom primary key for the database.
I'm asked to write test cases for the whole application. While I think that code coverage on SonaqQube can't be 100%, still I'm being asked to write the test cases for the whole application.
I've gone through the source code for SequenceStyleGenerator but I'm unable to get it how to test the class.
Here is the code for the same.
public class BigIntegerSequenceGenerator extends SequenceStyleGenerator {
public static final String VALUE_PREFIX_PARAMETER = "valuePrefix";
public static final String VALUE_PREFIX_DEFAULT = "";
private String valuePrefix;
public static final String NUMBER_FORMAT_PARAMETER = "numberFormat";
public static final String NUMBER_FORMAT_DEFAULT = "%d";
private String numberFormat;
#Override
public Serializable generate(SharedSessionContractImplementor session,
Object object) {
return valuePrefix + String.format(numberFormat, super.generate(session, object));
}
#Override
public void configure(Type type, Properties params,
ServiceRegistry serviceRegistry) {
super.configure(LongType.INSTANCE, params, serviceRegistry);
valuePrefix = ConfigurationHelper.getString(VALUE_PREFIX_PARAMETER,
params, VALUE_PREFIX_DEFAULT);
numberFormat = ConfigurationHelper.getString(NUMBER_FORMAT_PARAMETER,
params, NUMBER_FORMAT_DEFAULT);
}
}
Here is the test for this file
class BigIntegerSequenceGeneratorTest {
SharedSessionContractImplementor session;
BigIntegerSequenceGenerator generator;
#BeforeEach
void setUp() {
generator = new BigIntegerSequenceGenerator();
session = mock(SharedSessionContractImplementor.class);
session = mock(Session.class);
}
#Test
void testGenerate() {
generator.generate(session,new Object());
}
}
I'm getting
java.lang.NullPointerException
at org.hibernate.id.enhanced.SequenceStyleGenerator.generate(SequenceStyleGenerator.java:520)
I check it by using debug points and this is the code I'm getting.
#Override
public Serializable generate(SharedSessionContractImplementor session, Object object) throws HibernateException {
return optimizer.generate( databaseStructure.buildCallback( session ) );
}
optimizer and databaseStructure are null.
For what I know, when the SpringBoot application runs, it automatically configures the hibernate and the optimizer and databaseStruictore are configured which will then be used for the models when saving the data.
I know that this will require the database connection mocking but it seems like I'm unable to do it.
NOTE : I'm using JUnit5 for testing
-Thank you
I am using Spring Data to access a Mongo database in a Spring Boot application. I'm doing this by extending MongoRepository.
#CrudPublishingRepository
public interface ProfileRepository extends MongoRepository<Profile, String> {
Optional<Profile> findByUserName(String userName);
#Query("{'products.contracts.contractId': ?0}")
List<Profile> findByContractId(String contractId);
}
For some of my repositories (including the ProfileRepository above) I need to perform some actions every time a save or delete action is performed, which is why I created the #CrudPublishingRepository annotation.
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
#Documented
public #interface CrudPublishingRepository {}
I have used Spring AOP to intercept all save and delete methods on Spring's CrudRepository, with the following advices:
#AfterReturning("execution(public * delete(..)) && this(org.springframework.data.repository.CrudRepository)")
public void onDeleteExecuted(JoinPoint pjp) {
onDelete(pjp);
}
#AfterReturning("execution(public * deleteById(..)) && this(org.springframework.data.repository.CrudRepository)")
public void onDeleteByIdExecuted(JoinPoint pjp) {
onDeleteById(pjp);
}
#AfterReturning("execution(public * deleteAll(..)) && this(org.springframework.data.repository.CrudRepository)")
public void onDeleteAllExecuted(JoinPoint pjp) {
onDeleteAll(pjp);
}
#Around(value = "execution(public * save(..)) && this(org.springframework.data.repository.CrudRepository)")
public Object onSaveExecuted(ProceedingJoinPoint pjp) throws Throwable {
return onSave(pjp);
}
the onSave method looks like this:
private Object onSave(ProceedingJoinPoint pjp) throws Throwable {
System.out.println(getTopicName(pjp));
try {
Object result = pjp.proceed();
return result;
} catch (Throwable t) {
throw t;
}
}
And at last, the getTopicName(JoinPoint pjp) function, which is where the problem is:
private Optional<String> getTopicName(JoinPoint pjp) {
Class clazz = pjp.getTarget().getClass();
while (clazz != null) {
for (Class i : pjp.getTarget().getClass().getInterfaces()) {
if (i.getAnnotation(CrudPublishingRepository.class) != null) {
return Optional.of("found it!");
}
}
clazz = clazz.getSuperclass();
}
return Optional.empty();
}
The implementation obviously isn't finished yet, but I would expect it to return Optional.of("found it!"), but it doesn't.
When I debug, I can see that ProfileRepository is one of the interfaces, as expected, but getAnnotations() returns an empty array.
Can anyone offer me a solution or an explanation as to why this is not working?
In the React Native library theres a class https://github.com/facebook/react-native/blob/master/ReactAndroid/src/main/java/com/facebook/react/bridge/Arguments.java that is used for Bundles. I'm using PowerMockito to mock the Arguments.createMap() method and return an object with the following snippet:
PowerMockito.when(Arguments.createMap()).thenAnswer(
new Answer<Object>() {
#Override
public Object answer(InvocationOnMock invocation) throws Throwable {
return new JavaOnlyMap();
}
});
The method I'm testing errors with the following message when the test is ran:
java.lang.UnsatisfiedLinkError: no reactnativejni in java.library.path
when executing this line:
WritableMap map = Arguments.createMap();
Any ideas?
Extract the Answer<Object> to a variable. Mockito doesnt like it when you use the new operator as a parameter.
Try something like this:
Answer<Object> answer = new Answer<Object>() {
#Override
public Object answer(InvocationOnMock invocation) throws Throwable {
return new JavaOnlyMap();
}
}
PowerMockito.when(Arguments.createMap()).thenAnswer(answer);
Code coverage of Resultsetextractor anonymous class is not covered.
There is no excecption at run time , it is running fine.
We have mocked the jdbcTemplate using below sample code.
Mockito.when(mainTemplate.query(QUERY, new HashMap(),rs)).thenReturn(new Object());
Below Code (start with #Override annotation - extractData() method) is not covered after execution.
return mainTemplate.query(Query, paramMap, new ResultSetExtractor>() {
**#Override
public Map<String, Object> extractData(ResultSet resultSet)
throws SQLException, DataAccessException {
Map<String, Object> keyMap = new HashMap<String, Object>();
while (resultSet.next()) {
keyMap.put("key1",
resultSet.getString("data1"));
keyMap.put("key2", resultSet.getString("data2"));
}
return keyMap;
}**
});
Please help.
Please check sample code for your information.
**return jdbcTemplatre.query(Query, paramMap, new ResultSetExtractor<byte[]>() {
#Override
public byte[] extractData(ResultSet resultSet) throws SQLException,
DataAccessException {
byte[] data = null;
while (resultSet.next()) {
signature = resultSet.getBytes("data");
}
return data;
}
});**
Mockito.when(mainTemplate.query(Query, new HashMap<String,Object>(),rs)).thenAnswer(new Answer<Object>(){
#Override
public byte[] answer(InvocationOnMock invocationOnMock) throws Throwable {
ResultSetExtractor resultSetExtractor = invocationOnMock.getArgumentAt(2,ResultSetExtractor.class);
return (byte[]) resultSetExtractor.extractData(resSet);
}
});
The ResultSetExtractor code is not covered because it is never executed. And it is not executed because the mainTemplate.query call is mocked thus the ResultSetExtractor callback is never triggered.
You could triggered it while still using the mock by using Mockitos thenAnswer
Mockito.when(mainTemplate.query(QUERY, new HashMap(),rs)).thenAnswer(new Answer<Object>() {
#Override
public String answer(InvocationOnMock invocationOnMock) throws Throwable {
ResultSetExtractor resultSetExtractor = invocationOnMock.getArgumentAt(2, ResultSetExtractor.class);
resultSetExtractor.extractData(...);
return new Object();
}
});
Or with java 8
Mockito.when(mainTemplate.query(QUERY, new HashMap(),rs)).thenAnswer(invocationOnMock -> {
ResultSetExtractor resultSetExtractor = invocationOnMock.getArgumentAt(2, ResultSetExtractor.class);
resultSetExtractor.extractData(...);
return new Object();
});
But testing this way you would have to create a ResultSet yourself.
But generally speaking: this way of calling resultSetExtractor.extractData in the mock might cause problems because it is making assumptions about how the jdbcTemplate.query method handles the ResultSetExtractor internally. But that behaviour might change and then your test does not represent anymore what actually happens in production.
I am trying to write a unit test for a AWS SWF workflow. Below is the code I would like to Test
#Override
public void execute(String abc) {
new TryCatch() {
#Override
protected void doTry() throws Throwable {
Promise<SomeObject> temp = activityClient.action(abc);
again(temp, abc);
}
#Override
protected void doCatch(Throwable e) throws Throwable {
throw new RuntimeException(e);
}
};
}
#Asynchronous
public void again(Promise<SomeObject> someObject, String abc) {
// Do Something
}
My Test class is as below:
public class SomeWorkflowTest extends AbstractTestCase {
#Rule
public WorkflowTest workflowTest = new WorkflowTest();
List<String> trace;
private SomeWorkflowClientFactory workflowFactory = new SomeWorkflowClientFactoryImpl();
#Before
public void setUp() throws Exception {
trace = new ArrayList<String>();
// Register activity implementation to be used during test run
SomeActivitiesImpl activitiesImpl = new SomeActivitiesImpl() {
#Override
public SomeObject performHostRecovery(String abc) {
trace.add("ABC: " + abc);
SomeObject testObject = new SomeObject();
return testObject;
}
};
workflowTest.addActivitiesImplementation(activitiesImpl);
workflowTest.addWorkflowImplementationType(SomeWorkflowImpl.class);
}
#Test
public void testWorkflowExecutionCall() throws Throwable {
SomeWorkflowClient workflow = workflowFactory.getClient("XZY");
workflow.execute("XYZ");
List<String> expected = new ArrayList<String>();
expected.add("ABC: abc");
AsyncAssert.assertEqualsWaitFor("Wrong Wrong", expected, trace, null);
}
}
I have used SWF Testing Docs to write above test class. However the method that I am testing (execute()) is invoking another method in same class. I am not concerned with the execution of internal method and would like to mock it out, but given the way the workflow class object is instantiated, I am not clear on how to mock the inner method.
Can someone please point out on this?
Thanks
You actually can instantiate a workflow object or any other object that workflow uses inside the test method:
#Test
public void testWorkflowExecutionCall() throws Throwable {
SomeWorkflow workflow = new SimpleWorkflow(...);
workflow.execute("XYZ");
List<String> expected = new ArrayList<String>();
expected.add("ABC: abc");
AsyncAssert.assertEqualsWaitFor("Wrong Wrong", expected, trace, null);
}
It works because WorkflowTest executes test methods in the context of a dummy test workflow. The code
SomeWorkflowClient workflow = workflowFactory.getClient("XZY");
workflow.execute("XYZ");
actually creates a child workflow in the context of this dummy workflow. But nothing prevents you from executing any async code directly without creating the child workflow.