Mockito / Spring MVC - (annotation-driven) request mapping dynamic verification - java

I'm currently writing a Spring MVC-based webapp.
Rather than writing one test for every annotated method, I would like to benefit from Parameterized JUnit runner.
Finally, I got it almost working, although I had to change all primitive arguments to their wrapper counterpart in my controller methods (and then manually do the sanity checks on null refs).
If it can help, here is the code (this also depends on Guava):
#RunWith(Parameterized.class)
public class MyControllerMappingTest {
private MockHttpServletRequest request;
private MockHttpServletResponse response;
private MyController mockedController;
private AnnotationMethodHandlerAdapter annotationHandlerAdapter;
private final String httpMethod;
private final String uri;
private final String controllerMethod;
private final Class<?>[] parameterTypes;
private final Object[] parameterValues;
#Before
public void setup() {
request = new MockHttpServletRequest();
response = new MockHttpServletResponse();
mockedController = mock(MyController.class);
annotationHandlerAdapter = new AnnotationMethodHandlerAdapter();
}
#Parameters
public static Collection<Object[]> requestMappings() {
return asList(new Object[][] {
{"GET", "/my/uri/0", "index", arguments(new MethodArgument(Integer.class, 0))}
});
}
private static List<MethodArgument> arguments(MethodArgument... arguments) {
return asList(arguments);
}
public MyControllerMappingTest(String httpMethod, String uri, String controllerMethod, List<MethodArgument> additionalParameters) {
this.httpMethod = httpMethod;
this.uri = uri;
this.controllerMethod = controllerMethod;
this.parameterTypes = new Class<?>[additionalParameters.size()];
initializeParameterTypes(additionalParameters);
this.parameterValues = newArrayList(transform(additionalParameters, valueExtractor())).toArray();
}
private void initializeParameterTypes(List<MethodArgument> additionalParameters) {
Iterable<Class<?>> classes = transform(additionalParameters, typeExtractor());
int i = 0;
for (Class<?> parameterClass : classes) {
parameterTypes[i++] = parameterClass;
}
}
#Test
public void when_matching_mapping_constraints_then_controller_method_automatically_called() throws Exception {
request.setMethod(httpMethod);
request.setRequestURI(uri);
annotationHandlerAdapter.handle(request, response, mockedController);
Method method = MyController.class.getMethod(controllerMethod, parameterTypes);
method.invoke(verify(mockedController), parameterValues);
}
}
with the custom class MethodArgument that follows:
public class MethodArgument {
private final Class<?> type;
private final Object value;
public MethodArgument(final Class<?> type, final Object value) {
this.type = type;
this.value = value;
}
public Object getValue() {
return value;
}
public Class<?> getType() {
return type;
}
public static Function<MethodArgument, Class<?>> typeExtractor() {
return new Function<MethodArgument, Class<?>>() {
#Override
public Class<?> apply(MethodArgument argument) {
return argument.getType();
}
};
}
public static Function<MethodArgument, Object> valueExtractor() {
return new Function<MethodArgument, Object>() {
#Override
public Object apply(MethodArgument argument) {
return argument.getValue();
}
};
}
}
So, I'm almost there, the only test case here works because of Java Integer cache, and the Integer instance is therefore the same throughout the call chain... This however doesn't work with custom objects, I always end up with an InvocationTargetException (cause: "Argument(s) are different!")...
The types are correct but the passed instances are not identical to the ones set in the #Parameters method.
Any idea how to work around this?

Hold your horses!
SpringSource is baking a spring-test-mvc module :
https://github.com/SpringSource/spring-test-mvc

It would be nice if instead of providing the example that works, you could provide the one that doesn't, and provide the stacktrace as well.
I quickly checked Google, it seems that Mockito doesn't handle well reflection on spy objects.
If you really wanna go along that road, there might be another way: providing the expected called method as part of your parameterized data, not by providing reflection data, but by actually calling the mock from there.
I'm writing that without any IDE at hand, so there might be compile errors, but you'll get the idea:
#RunWith(Parameterized.class)
public class MyControllerMappingTest {
public interface VerifyCall<T> {
void on(T controller);
}
#Parameters
public static Collection<Object[]> requestMappings() {
Object[][] testCases = {
{"GET", "/my/uri/0", new VerifyCall<MyController>() {
#Override
public void on(MyController controller) {
controller.index(0);
}
}}
};
return asList(testCases);
}
private MockHttpServletRequest request;
private MockHttpServletResponse response;
private MyController mockedController;
private AnnotationMethodHandlerAdapter annotationHandlerAdapter;
private final String httpMethod;
private final String uri;
private final VerifyCall<MyController> verifyCall;
public MyControllerMappingTest(String httpMethod, String uri, VerifyCall<MyController> verifyCall) {
this.httpMethod = httpMethod;
this.uri = uri;
this.verifyCall = verifyCall;
}
#Before
public void setup() {
request = new MockHttpServletRequest();
response = new MockHttpServletResponse();
mockedController = mock(MyController.class);
annotationHandlerAdapter = new AnnotationMethodHandlerAdapter();
}
#Test
public void when_matching_mapping_constraints_then_controller_method_automatically_called() throws Exception {
request.setMethod(httpMethod);
request.setRequestURI(uri);
annotationHandlerAdapter.handle(request, response, mockedController);
verifyCall.on(verify(mockedController));
}
}
Of course, having Java Lambas would help making this more readable.
You could also use FunkyJFunctional :
#RunWith(Parameterized.class)
public class MyControllerMappingTest {
#Parameters
public static Collection<Object[]> requestMappings() {
class IndexZero extends FF<MyController, Void> {{ in.index(0); }}
Object[][] testCases = { //
{"GET", "/my/uri/0", withF(IndexZero.clas)}
};
return asList(testCases);
}
private MockHttpServletRequest request;
private MockHttpServletResponse response;
private MyController mockedController;
private AnnotationMethodHandlerAdapter annotationHandlerAdapter;
private final String httpMethod;
private final String uri;
private final Function<MyController, Void> verifyCall;
public MyControllerMappingTest(String httpMethod, String uri, Function<MyController, Void> verifyCall) {
this.httpMethod = httpMethod;
this.uri = uri;
this.verifyCall = verifyCall;
}
#Before
public void setup() {
request = new MockHttpServletRequest();
response = new MockHttpServletResponse();
mockedController = mock(MyController.class);
annotationHandlerAdapter = new AnnotationMethodHandlerAdapter();
}
#Test
public void when_matching_mapping_constraints_then_controller_method_automatically_called() throws Exception {
request.setMethod(httpMethod);
request.setRequestURI(uri);
annotationHandlerAdapter.handle(request, response, mockedController);
verifyCall.apply(verify(mockedController));
}
}
A few side notes:
For the sake of readability, it's a good practice to put your static members first in your class. Instance methods (setup()) should also go after the constructor.
Array syntax:
Instead of this syntax:
return asList(new Object[][] {
{},
{}
};
I find this syntax to be more readable:
Object[][] testCases = {
{},
{}
};
return asList(testCases);

Related

How to get original Class type from Class object using ".class" in java?

As part of an abstract generic API function, I include a parameter of type "Class" in the constructor which is used to deserialise a response (in JSON format) into an object of that class type.
Each API call can have a different response class.
Currently, my parent class definition is:
public MyRequest {
private int method;
private String url;
private Class responseClass;
private #Setter JSONObject params;
public MyRequest(int method, String url, Class responseClass){
this.method = method;
this.url = url;
this.responseClass = responseClass;
}
public void executeRequest(){
new HttpJsonRequest(method,url,params) {
#Override
public void handleResponse(JSONObject response) {
Object responseObj = new Gson().fromJson(String.valueOf(response), responseClass);
onSuccess(responseObj);
}
...
}
}
public void onSuccess(Object response){}
...
}
An example API request that extends this:
public class ExampleRequest extends MyRequest {
public ExampleRequest(String exampleParam){
super(Request.Method.POST, "/example", MyResponse.class);
JSONObject params = new JSONObject();
params.put("exampleParam",exampleParam);
setParams(params);
executeRequest();
}
}
with basic response class:
#AllArgsConstructor
#NoArgsConstructor
public MyResponse {
private #Getter #Setter String StatusCode;
}
To use the above API class, I am overriding the onSuccess method which returns the Generic object and casting it back to the responseClass before proceeding, e.g.:
new ExampleRequest("exampleParamValue"){
#Override
public void onSuccess(Object response){
String StatusCode = ((MyResponse) response).getStatusCode();
...
}
}
Is there a way to amend MyRequest so that instead of responseObj being of class "Object", it can be of whatever class was passed into the constructor?
I'd like to avoid having to perform the cast from Object to this class in every onSucccess override I use.
You can solve this by making MyRequest generic, with a type parameter for the response type:
import com.google.gson.Gson;
import lombok.Setter;
import org.json.JSONObject;
public class MyRequest<R> {
private int method;
private String url;
private Class<R> responseClass;
private #Setter JSONObject params;
public MyRequest(int method, String url, Class<R> responseClass) {
this.method = method;
this.url = url;
this.responseClass = responseClass;
}
public void executeRequest() {
new HttpJsonRequest(method, url, params) {
#Override
public void handleResponse(JSONObject response) {
R responseObj = new Gson().fromJson(String.valueOf(response), responseClass);
onSuccess(responseObj);
}
};
}
public void onSuccess(R response) {
}
}
Then, ExampleRequest would specify MyResponse as the value of the parameter:
public class ExampleRequest extends MyRequest<MyResponse> {
// ... no other changes
}
The cast is then no longer required when implementing onSuccess:
new ExampleRequest("exampleParamValue"){
#Override
public void onSuccess(MyResponse response){
String StatusCode = response.getStatusCode();
}
};

How to deserialize to map or array class where map structure varies in Java?

I need to deserialize the response of an external REST API which is usually a JSON object but if an error occurred it will be an array of JSON objects. The API is lacking in documentation so the way I determine if error occurred or not is by the HTTP Status of the response. The problem is that non-error responses have different structure per API route (user response, product response etc.). Another problem is in my application we use an abstract class for external API response with such fields as error, hasError. I solved the problem as follows:
public abstract class AbsractApiResponse {
public abstract String getError();
public abstract void setError(String error);
public abstract String getDescription();
public abstract void setDescription(String error);
}
public class ErrorResponse {
private String errorCode; // getters/setters
private String message; // getters/setters
}
public class UserResponse extends AbsractApiResponse {
private String error; // getters/setters
private boolean hasError; // getters/setters
private boolean description; // getters/setters
private String userName;
private String userEmail;
}
public <R extends AbsractApiResponse> R deserializeResponse(
String apiResponse, Class<R> responseType, boolean isHttpError)
throws JsonProcessingException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
R response;
Object deserialized = objectMapper.readValue(apiResponse, Object.class);
if (isHttpError && deserialized instanceof List) {
TypeReference<List<ErrorResponse>> errorResponseType = new TypeReference<>() {};
List<ErrorResponse> responseErrors = objectMapper.convertValue(deserialized,
errorResponseType);
Constructor<R> constructor = responseType.getDeclaredConstructor();
response = constructor.newInstance();
ErrorResponse firstError = responseErrors.get(0);
String errorDescription = responseErrors.stream().map(ErrorResponse::toString).collect(Collectors.joining());
response.setError(firstError.getMessage());
response.setDescription(errorDescription);
} else {
response = objectMapper.convertValue(deserialized, responseType);
}
return response;
}
With this approach I would have to add fields like error/hasError etc. to every class which represents a response which isn't that bad I guess. Another red flag for me is the use of reflection (responseType.getDeclaredConstructor()) and the 4 checked exceptions that go with it. I'm wondering, if there's a better way to solve this?
I do not recommend to merge error response together with business objects. You can return given response class in case of success and throw an exception in case of error. This is what I think would be the cleanest way.
If you do not want to throw an exception you can implement wrapper class which contains response and error objects. In case error field is set we know there was a problem. It could look like below:
interface ApiResponse {
}
#Data
class ResponseWrapper<R extends ApiResponse> {
private R response;
private Error error;
public boolean hasErrors() {
return Objects.nonNull(error);
}
}
#Data
class Error {
private String error;
private String description;
}
#Data
class ErrorResponse {
private String errorCode;
private String message;
}
#Data
class UserResponse implements ApiResponse {
private String userName;
private String userEmail;
}
And generic implementation of that method could look like:
class JsonDecoder {
private final ObjectMapper objectMapper = ...;
public <R extends ApiResponse> ResponseWrapper<R> deserializeResponse(String apiResponse, Class<R> responseType, boolean isHttpError)
throws JsonProcessingException {
ResponseWrapper<R> response = new ResponseWrapper<>();
if (isHttpError) {
response.setError(deserializeError(apiResponse));
} else {
response.setResponse(objectMapper.readValue(apiResponse, responseType));
}
return response;
}
private Error deserializeError(String apiResponse) throws JsonProcessingException {
final TypeReference<List<ErrorResponse>> errorResponseType = new TypeReference<>() {};
List<ErrorResponse> errors = objectMapper.readValue(apiResponse, errorResponseType);
ErrorResponse firstError = errors.get(0);
String errorDescription = errors.stream().map(ErrorResponse::toString).collect(Collectors.joining());
Error error = new Error();
error.setError(firstError.getMessage());
error.setDescription(errorDescription);
return error;
}
}

Mockito showing 0 interactions with mock

Here is my code:
public class S3Dao {
private final AmazonS3Client amazonS3Client;
static final String BUCKET_NAME = "myBucket";
public S3Dao(final AmazonS3Client amazonS3Client) {
this.amazonS3Client = amazonS3Client;
}
public void put(ModelObject modelObject, String playlistId) {
this.amazonS3Client.putObject(BUCKET_NAME, playlistId, new Gson().toJson(modelObject));
}
}
And my test code:
#ExtendWith(MockitoExtension.class)
public class S3DaoTest {
private S3Dao s3Dao;
#Mock
private AmazonS3Client s3Client;
#BeforeEach
public void beforeEach() {
this.s3Dao = new S3Dao(this.s3Client);
}
#Test
public void putTest() {
ModelObject obj = new ModelObject("name", new ArrayList<>());
String json = new Gson().toJson(obj);
verify(s3Client).putObject(S3Dao.BUCKET_NAME, "playlistId", json);
this.s3Dao.put(obj, "playlistId");
}
}
The resulting error is
Wanted but not invoked:
s3Client.putObject(
"myBucket",
"playlistId",
"{"name":"name","children":[]}"
);
-> at com.amazon.amazon.live.destination.playlist.dao.S3DaoTest.putTest(S3DaoTest.java:34)
Actually, there were zero interactions with this mock.
Indeed, removing the verify causes the test to pass without issue. Why isn't this working?
Move the verify after the method call
#Test
public void putTest() {
ModelObject obj = new ModelObject("name", new ArrayList<>());
String json = new Gson().toJson(obj);
this.s3Dao.put(obj, "playlistId");
verify(s3Client).putObject(S3Dao.BUCKET_NAME, "playlistId", json);
}

QueryDsl avoiding multiple if blocks

Currently I am using Query DSL in my Java(with JPA) EE project. I recieve a filterObject from UI as json with all the filters. My FilterObject looks like this
public class FilterObject {
private String name;
private List<Status> status;
private String module;
private List<Source> source;
......
}
And in my service class I have something like this
public List<MyModel> findByFilter(FilterObject filterObject) {
BooleanBuilder builder = new BooleanBuilder();
QMyModel mymodel= QMyModel.myModel;
if(filterObject.getName() != null) {
builder.and(mymodel.name.contains(filterObject.getName()));
}
if(! CollectionUtils.isEmpty(filterObject.getStatus())) {
builder.and(mymodel.status.in(filterObject.getStatus()));
}
...............
...............
}
And finally I have this
JPAQuery<MyModel> query = new JPAQuery<>(getEntityManager());
List<MyModel> myModels = query.from(QMyModel.mymodel).where(builder).fetch();
EDIT:
/**
* QMyModel is a Querydsl query type for MyModel
*/
#Generated("com.querydsl.codegen.EntitySerializer")
public class QMyModel extends EntityPathBase<MyModel> {
private static final long serialVersionUID = 1041638507L;
private static final PathInits INITS = PathInits.DIRECT2;
public static final QMyModel myModel = new QMyModel("myModel");
public final StringPath name = createString("name");
public final EnumPath<Status> status = createEnum("status", Status.class);
public final StringPath module = createString("module");
........
.......
}
All these work. But my FilterObject is growing and has more than 10 fields. So I have like 10 If blocks in my service class method. Is there a better way to do this where I could avoid so many if blocks.
You can use lambda's, or (even better in this case) method reference:
public List<MyModel> findByFilter(FilterObject filterObject) {
BooleanBuilder builder = new BooleanBuilder();
QMyModel mymodel = QMyModel.myModel;
add(builder, filterObject.getName(), mymodel.name::contains);
add(builder, filterObject.getStatus(), mymodel.status::in);
...
}
private <T> void add(BooleanBuilder builder, T filterElement, Function<T, BooleanExpression> booleanExpressionFunction) {
if (valid(filterElement)) {
builder.and(booleanExpressionFunction.apply(filterElement));
}
}
private boolean valid(Object filterElement) {
if (filterElement == null) {
return false;
}
if (filterElement instanceof Collection) {
return !((Collection) filterElement).isEmpty();
}
return true;
}

Java Generic relating to base and child class "not applicable for the arguments"

I did found come similar looking posts on generic issues, but non of them captured my problem in a way I could understand it. I hope someone can help me with this case.
I tried lots of things like removing the "? extends" from different places and playing with "? super" to see if another error would point me in a direction of a solutions.
The following code:
final ResponseBase response = executor.execute(myRequest);
Gives me the following compiler error:
"The method execute(capture#6-of ? extends RequestBase) in the type CommandExecutor is not applicable for the arguments (RequestBase)"
The complete code listing:
public class MainClass {
private final static Map<Class<? extends RequestBase>, CommandExecutor<? extends RequestBase, ? extends ResponseBase>> MAP = new HashMap<>();
public static void main(String[] args) {
final DummyCommandExecutor dummyCommandExecutor = new DummyCommandExecutor();
MAP.put(MyRequest.class, dummyCommandExecutor);
final RequestBase myRequest = new MyRequest();
myRequest.setRequestString("this is my request");
final CommandExecutor<? extends RequestBase, ? extends ResponseBase> executor = MAP.get(myRequest.getClass());
final ResponseBase response = executor.execute(myRequest);
System.out.println(response.getResponseString());
}
}
interface CommandExecutor<T, R> {
R execute(T object);
}
class DummyCommandExecutor implements CommandExecutor<MyRequest, MyResponse> {
#Override
public MyResponse execute(MyRequest request) {
final MyResponse response = new MyResponse();
response.setResponseString(request.getRequestString());
return response;
}
}
class MyResponse extends ResponseBase {
}
class ResponseBase {
String responseString;
public String getResponseString() {
return this.responseString;
}
public void setResponseString(String responseString) {
this.responseString = responseString;
}
}
class MyRequest extends RequestBase {
}
class RequestBase {
String requestString;
public String getRequestString() {
return this.requestString;
}
public void setRequestString(String requestString) {
this.requestString = requestString;
}
}
You can't do this without casting. While you know that the map will return the correct command executor for a given type, the compiler does not, so you need to tell the compiler to not care:
final CommandExecutor<RequestBase,ResponseBase> executor = (CommandExecutor) MAP.get(myRequest.getClass());
you cannot just match ? with T
an option is just to do :
final CommandExecutor executor = MAP.get(myRequest.getClass());
in which option of course you can get run time exception if the map returns smth not castable

Categories

Resources