package org.camunda.bpm.extension.mockito.query;
import static org.mockito.Mockito.when;
import javax.annotation.Nonnull;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;
import org.camunda.bpm.engine.query.Query;
import org.camunda.bpm.extension.mockito.answer.FluentAnswer;
/**
* This looks more complicated than it actually is ... To easily mock the
* behaviour of queries, all common functionality is extracted to this abstract
* super class, the high level of Generics is needed for the fluent api pattern,
* so the abstract mock implementing the generic Query interface must also keep
* a reference to itself.
*
* @param <M>
* the type of the AbstractQueryMock (repeat the type of the class you
* are building). Used to "return this"
* @param <Q>
* the type of the query to mock (for example: TaskQuery).
* @param <R>
* the type of the expected result (for example: Execution).
* @param <S>
* the type of the service the query belongs to (used for "forService"
* binding), for Example: TaskService.
*
* @author Jan Galinski
*/
abstract class AbstractQueryMock<M extends AbstractQueryMock<M, Q, R, S>, Q extends Query<?, R>, R extends Object, S> implements Supplier<Q> {
/**
* The internally stored query instance.
*/
private final Q query;
/**
* Used for mocking the query creation via reflection.
*/
private final Method createMethod;
/**
* Creates a new query mock and mocks fluent api behavior by adding a default
* answer to the mock. Every createMethod will return the mock itself, except
* <ul>
* <li>list() - returns empty ArrayList</li>
* <lI>singeResult() - returns null</lI>
* </ul>
*
* @param queryType
* the type of the query to mock.
* @param serviceType
* the type of service that generates this query
*/
protected AbstractQueryMock(@Nonnull final Class<Q> queryType, @Nonnull final Class<S> serviceType) {
query = FluentAnswer.createMock(queryType);
createMethod = createMethod(queryType, serviceType);
list(new ArrayList<R>());
singleResult(null);
}
private Method createMethod(@Nonnull final Class<Q> queryType, @Nonnull Class<S> serviceType) {
try {
return serviceType.getDeclaredMethod("create" + queryType.getSimpleName());
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
public Q list(final List<R> result) {
when(query.list()).thenReturn(result);
return get();
}
public Q singleResult(final R result) {
when(query.singleResult()).thenReturn(result);
return get();
}
public Q count(long result) {
when(query.count()).thenReturn(result);
return get();
}
public Q listPage(List<R> result, int min, int max) {
when(query.listPage(min,max)).thenReturn(result);
return get();
}
public M forService(S service) {
try {
when(createMethod.invoke(service)).thenReturn(get());
} catch (Exception e) {
throw new RuntimeException(e);
}
return (M) this;
}
@Override
public final Q get() {
return query;
}
}