how to instantiate objects inside the class to be tested is an abstract class in Junit testing? - junit

I have a class below for which I want to write a unit test
abstract class ProductImpl{
#Inject DataServices ds; // using Guice
public Response parse(String key, Long value){
Response res = ds.getResponseObject(); // Response object is created using DataServices object
res.id = key;
res.code = value;
}
}
And I have a test as below
class ProductImplTest{
#InjectMocks ProductImpl impl;
Map<String, Long> map;
#Before
map.put("abc", 10L);
map.put("xyz", 11L);
}
#Test
public void test(){
for(String key: map.keySet()){
Response res = impl.parse(key, map.get(key));
// and check if fields of Response object are set correctly i.e res.id is abc and value is 10L
}
}
But when i debug the test and control goes to parse method , DataServices object ds is null. How to instantiate this object through test . I do not want to use mocking, I want real response objects to be created and test the values set in them.

You can use Mockito
#RunWith(MockitoJUnitRunner.class)
class ProductImplTest {
#Mock DataService dService;
#InjectMocks ProductImpl sut;
#Test
public void test() {
ResponseObject ro = new ResponseObject();
String string = "string";
Long longVal = Long.valueOf(123);
sut.parse("string", longVal);
verify(dService).getResponseObject();
assertThat(ro.getId()).isEqualTo("string");
// you should use setters (ie setId()), then you can mock the ResponseObject and use
// verify(ro).setId("string");
}
}
EDIT:
With ResponseObject being an abstract class or preferably an interface, you'd have
interface ResponseObject {
void setId(String id);
String getId();
// same for code
}
and in your test
#Test public void test() {
ResponseObject ro = mock(ResponseObject.class);
// ... same as above, but
verify(dService).getResponseObject();
verify(ro).setId("string"); // no need to test getId for a mock
}

Try with constructor injection:
class ProductImpl{
DataServices ds;
#Inject
public ProductImpl(DataServices ds) {
this.ds = ds;
}
}

Related

Mock not initiated on Static method

I am facing issues in mocking static method.
Below is my code where I am calling a static method
public class GetAllBatches {
public HttpResponseMessage run(
#HttpTrigger(route = "v1/batches",
name = "request",
methods = {HttpMethod.GET},
authLevel = AuthorizationLevel.ANONYMOUS)
HttpRequestMessage<String> request,
final ExecutionContext context){
context.getLogger().info("List batches Called");
String apiResponse ;
String connector = request.getQueryParameters().getOrDefault("connector", "");
try{
BatchesController batchesController = BatchesController.getInstance();
apiResponse = new Gson().toJson(batchesController.getBatches(connector));
}
}
}
BatchesController Class :
public class BatchesController {
Logger log = Logger.getLogger(BatchesController.class.getName());
public static BatchesController getInstance() {
if (batchesController == null) {
batchesController = new BatchesController(BatchDaoFactory.getDao());
}
return batchesController;
}
private static BatchesController batchesController = new BatchesController();
private final BatchDao batchDao;
public BatchesController(BatchDao BatchDao) {
this.batchDao = BatchDao;
}
// Do something
}
And below is the test that I have :
#RunWith(MockitoJUnitRunner.class)
public class GetAllBatchesTest {
#Mock
ExecutionContext context;
#Mock
HttpRequestMessage<String> request;
#Mock
BatchesController batchesController;
#Mock
BatchDao BatchDao;
#InjectMocks
GetAllBatches getAllBatchesMock = new GetAllBatches();
#Before
public void setUp() {
Map<String, String> map = new HashMap<>();
map.put("connector", "");
doReturn(Logger.getGlobal()).when(context).getLogger();
doReturn(map).when(request).getQueryParameters();
try (MockedStatic<BatchesController> utilities = Mockito.mockStatic(BatchesController.class)) {
utilities.when(BatchesController::getInstance).thenReturn(batchesController);
}
doAnswer((Answer<HttpResponseMessage.Builder>) invocationOnMock -> {
HttpStatus status = (HttpStatus) invocationOnMock.getArguments()[0];
return new HttpResponseMessageMock.HttpResponseMessageBuilderMock().status(status);
}).when(request).createResponseBuilder(any(HttpStatus.class));
}
#Test
public void testHttpTriggerJava() {
final HttpResponseMessage ret = getAllBatchesMock.run(request, context);
Assertions.assertEquals(ret.getStatus(), HttpStatus.OK);
}
When I run my test, it throws an error message :
java.lang.ExceptionInInitializerError
BatchesController.getInstance() is not actually returning the mock value.
I am not sure what is going wrong here ?
UPDATE :
I found out that the problem is because I am using Mockito-inline Mockito-inline fails to initiate mock on class but initiates mock only on interfaces
You are using a try-with-resources block to setup a static mock:
try (MockedStatic<BatchesController> utilities = Mockito.mockStatic(BatchesController.class)) {
utilities.when(BatchesController::getInstance).thenReturn(batchesController);
}
Remember that the static mock is only active in scope of the block - after you exit the block the resource is closed.
Thus, you need to:
move the static mock initialization from setup method to the test method
run code under test within the try-with-resources block

mocking an autowired object and calling a method from that object

I have a class like this shown below:
public class Service {
#Value("${value1"})
String val1;
#Value("${value2"})
String val2;
#Value("${value1"})
String val3;
#Autowired
Client obj;
public String getResponse(String location, WebRequest request) {
// getting invocation target exception
String id = (String)request.getAttribute(Globals.id, request.SCOPE_REQUEST);
Point p = obj.getPoint(new Id(val1, val2, val3), "some string");
// do something
return getReply(id);
}
}
My test class is shown below:
public class ServiceTest {
#Mock
WebRequest request;
#Mock
Service service;
#Mock
Client obj;
#Before
public void setup() throws Exception {
}
#Test
public void testGetResponse() throws Exception {
when(request.getAttribute(Matchers.anyString(), Matchers.anyInt()).thenReturn("shbchdbchd");
when(obj.getPoint(Id.valueOf("id"), "some string").thenReturn("shbchdbchd");
service.getResponse("some location",request);
}
But the when(obj.getPoint) is not working, the parameters are null in the actual call in the class Service. This line
obj.getPoint(new Id(val1, val2, val3), "some string");
is getting null parameters.
It seems like you're using Spring in your Service class and no Spring in your unit test. As you're at the moment mocking all member variables in the class ServiceTest the unit test doesn't really test anything from the Service class.
A solution would be to manually setup your service instance in the unit test.
public class Service {
//....
#Autowired
public Service (
#Value("${value1"}) String value1,
#Value("${value2"}) String value2,
#Value("${value3"}) String value3,
Client client
){
this.val1=value1;
this.val2=value2;
this.val3=value3;
this.obj=client;
}
//....
public String getResponse(String location, WebRequest request) {
// getting invocation target exception
String id = (String)request.getAttribute(Globals.id, request.SCOPE_REQUEST);
//should you pass id instead of val1,val2,val3?
Point p = obj.getPoint(new Id(val1, val2, val3), "some string");
// do something
return getReply(id);
}
}
public class ServiceTest {
#Mock
WebRequest request;
#Mock
Client obj;
Service service;
#Before
public void setup() throws Exception {
service = new Service("value1","value2","value3",obj);
}
#Test
public void testGetResponse() throws Exception {
when(request.getAttribute(Matchers.anyString(), Matchers.anyInt()).thenReturn("shbchdbchd");
when(obj.getPoint(Id.valueOf("value1","value2","value3"), "some string").thenReturn("shbchdbchd");
service.getResponse("some location",request);
}
but to me it's still unclear what you really want to test.

Unable to run Mockito junit test

I am very new to JUnit. I wrote a simple JUnit test case which gives NullPointerException:
#RunWith(MockitoJUnitRunner.class)
public class ControllerTest {
#InjectMocks
Controller control;
#Mock
Service service;
#Test
public void postTest() throws IOException {
//Given
Object o = new Object();
//initialise the object here which the service layer would return
String a = "abcd";
Integer b = 5;
Mockito.when(service.Post(a,o)).thenReturn(o);
Mono<ResponseEntity<Object>> object = control.Post(a,o);
StepVerifier.create(object)
.consumeNextWith(entity -> {
assertEquals(b,entity.getBody().get());
})
.expectComplete();
I get a NullPointerException for this scenario.

How to dynamically ignore a property on Jackson serialization

I have an entity with multiple #ManyToOne associations. I am using spring-boot to expose a REST API. Currently, I have multiple REST API's which return a JSON response of the whole entity, including associations.
But I don't want to serialize all associated objects in all REST APIs.
For example
API-1 should return parent + associationA object
API-2 should return parent + associationA + associationB object
API-3 should return parent + associationB + associationC + associationD
So, in my serialization process, I want to ignore all association except associationA for API-1.
For API-2 I want to ignore other associations except A and B
How do I dynamically ignore these properties during Jackson serialization?
Notes:
I'm using the same class for each; I am not interested in creating a DTO for each API.
Any suggestions are kingly appreciated.
I've put together three approaches for performing dynamic filtering in Jackson. One of them must suit your needs.
Using #JsonView
You could use #JsonView:
public class Views {
interface Simple { }
interface Detailed extends Simple { }
}
public class Foo {
#JsonView(Views.Simple.class)
private String name;
#JsonView(Views.Detailed.class)
private String details;
// Getters and setters
}
#RequestMapping("/foo")
#JsonView(Views.Detailed.class)
public Foo getFoo() {
Foo foo = new Foo();
return foo;
}
Alternatively you can set the view dynamically with MappingJacksonValue.
#RequestMapping("/foo")
public MappingJacksonValue getFoo() {
Foo foo = new Foo();
MappingJacksonValue result = new MappingJacksonValue(foo);
result.setSerializationView(Views.Detailed.class);
return result;
}
Using a BeanSerializerModifier
You could extend BeanSerializerModifier and then override the changeProperties() method. It allows you to add, remove or replace any of properties for serialization, according to your needs:
public class CustomSerializerModifier extends BeanSerializerModifier {
#Override
public List<BeanPropertyWriter> changeProperties(SerializationConfig config,
BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) {
// In this method you can add, remove or replace any of passed properties
return beanProperties;
}
}
Then register the serializer as a module in your ObjectMapper:
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new SimpleModule() {
#Override
public void setupModule(SetupContext context) {
super.setupModule(context);
context.addBeanSerializerModifier(new CustomSerializerModifier());
}
});
Check examples here and here.
Using #JsonFilter with a SimpleBeanPropertyFilter
Another approach involves #JsonFilter:
#JsonFilter("customPropertyFilter")
public class Foo {
private String name;
private String details;
// Getters and setters
}
Extend SimpleBeanPropertyFilter and override the serializeAsField() method according to your needs:
public class CustomPropertyFilter extends SimpleBeanPropertyFilter {
#Override
public void serializeAsField(Object pojo, JsonGenerator jgen,
SerializerProvider provider,
PropertyWriter writer) throws Exception {
// Serialize a field
// writer.serializeAsField(pojo, jgen, provider, writer);
// Omit a field from serialization
// writer.serializeAsOmittedField(pojo, jgen, provider);
}
}
Then register the filter in your ObjectMapper:
FilterProvider filterProvider = new SimpleFilterProvider()
.addFilter("customPropertyFilter", new CustomPropertyFilter());
ObjectMapper mapper = new ObjectMapper();
mapper.setFilterProvider(filterProvider);
If you want to make your filter "global", that is, to be applied to all beans, you can create a mix-in class and annotate it with #JsonFilter("customPropertyFilter"):
#JsonFilter("customPropertyFilter")
public class CustomPropertyFilterMixIn {
}
Then bind the mix-in class to Object:
mapper.addMixIn(Object.class, CustomPropertyFilterMixIn.class);
public static <T> String getNonNullFieldsSerialized(T object, ObjectMapper objectMapper)throws JsonProcessingException {
Map<String, Object> objectMap = objectMapper.convertValue(object, new TypeReference<Map<String, Object>>() {});
Map<String, Object> objectMapNonNullValues = objectMap.entrySet().stream()
.filter(stringObjectEntry -> Objects.nonNull(stringObjectEntry.getValue()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
return objectMapper.writeValueAsString(objectMapNonNullValues);
}
This will basically ignore all the fields that are non-null. Similarly you can ignore other fields by changing the map filter condition.
I have implemented dynamic filter on data getting from db and returning it using rest api.I have avoided using MappingJacksonValue.As it was getting issue while object chaining
#GetMapping("/courses")
public ResponseEntity<JpaResponse> allCourse() throws Exception {
JpaResponse response = null;
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
List<Course> course = service.findAllCourse();
SimpleBeanPropertyFilter filter = SimpleBeanPropertyFilter.filterOutAllExcept("name","reviews");
FilterProvider filterProvider = new SimpleFilterProvider().addFilter("jpafilter", filter).setFailOnUnknownId(false);
ObjectWriter writer = mapper.writer(filterProvider);
String writeValueAsString = writer.writeValueAsString(course);
List<Course> resultcourse = mapper.readValue(writeValueAsString,List.class);
response = new JpaResponse(HttpStatus.OK.name(),resultcourse);
return new ResponseEntity<>(response, HttpStatus.OK);
}
public class JpaResponse {
private String status;
private Object data;
public JpaResponse() {
super();
}
public JpaResponse(String status, Object data) {
super();
this.status = status;
this.data = data;
}
}

Jersey client read json response into custom object

public class RESTDataServiceClient{
private Client client;
private String dataServiceUri;
private String dataServiceResource;
private CustomData customData;
public RESTDataServiceClient(String dataServiceUri, String dataServiceResource, Client client){
this.client = client;
this.dataServiceUri = dataServiceUri;
this.dataServiceResource = dataServiceResource;
}
#Override
public CustomData getCustomData() {
WebTarget dataServiceTarget = client.target(dataServiceUri).path(dataServiceResource);
Invocation.Builder invocationBuilder = dataServiceTarget.request(MediaType.APPLICATION_JSON_TYPE);
Response response = invocationBuilder.get();
myCustomData = response.readEntity(CustomData.class);
return myCustomData;
}
}
CustomData.java
public class CustomData{
private TLongObjectMap<Map<String, TIntIntMap>> data;
public CustomData() {
this.data = new TLongObjectHashMap<>();
}
//getter and setter
}
sample json content
{"50000":{"testString":{"1":10}},"50001":{"testString1":{"2":11}} }
I am trying to get data from a data service which is going to return data in a JSON format. I am trying to write a client to read that JSON into a custom object. The CustomData contains a nested trove map datastructure. we wrote a custom serializer for that and the server part works fine. I am unable to get the rest client read the data into an object, but reading into string works. I tried above pasted code with the sample data and i get the error below.
javax.ws.rs.ProcessingException: Error reading entity from input stream.
at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:866)
at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:783)
at org.glassfish.jersey.client.ClientResponse.readEntity(ClientResponse.java:326)
at org.glassfish.jersey.client.InboundJaxrsResponse$1.call(InboundJaxrsResponse.java:111)
at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
at org.glassfish.jersey.internal.Errors.process(Errors.java:228)
at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:399)
at org.glassfish.jersey.client.InboundJaxrsResponse.readEntity(InboundJaxrsResponse.java:108)
at com.sample.data.RESTDataServiceClient.getCustomData(RESTDataServiceClient.java:42)
Caused by: com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "50000" (class com.sample.data.CustomData), not marked as ignorable (0 known properties: ])
at [Source: org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream#2cb89281; line: 1, column: 14] (through reference chain: com.sample.data.CustomData["50000"])
at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:51)
at com.fasterxml.jackson.databind.DeserializationContext.reportUnknownProperty(DeserializationContext.java:671)
at com.fasterxml.jackson.databind.deser.std.StdDeserializer.handleUnknownProperty(StdDeserializer.java:773)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownProperty(BeanDeserializerBase.java:1297)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownVanilla(BeanDeserializerBase.java:1275)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:247)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:118)
at com.fasterxml.jackson.databind.ObjectReader._bind(ObjectReader.java:1233)
at com.fasterxml.jackson.databind.ObjectReader.readValue(ObjectReader.java:677)
at com.fasterxml.jackson.jaxrs.base.ProviderBase.readFrom(ProviderBase.java:777)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$TerminalReaderInterceptor.invokeReadFrom(ReaderInterceptorExecutor.java:264)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$TerminalReaderInterceptor.aroundReadFrom(ReaderInterceptorExecutor.java:234)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor.proceed(ReaderInterceptorExecutor.java:154)
at org.glassfish.jersey.message.internal.MessageBodyFactory.readFrom(MessageBodyFactory.java:1124)
at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:851)
... 38 more
TLongObjectMap is not deserializable out of the box, so how you made a custom serializer you also need to implement a custom deserializer. You can package these up nicely in a module and add it to your ObjectMapper.
It looks like there is a Trove module in development right now, which you can download and add to your ObjectMapper the same as the example below. The TIntObjectMapDeserializer implementation in that link is much more robust then my solution, so I would recommend using that class in your project if possible.
If you want to try and write it yourself, here's a starting point that properly deserializes your provided example:
public class FakeTest {
#Test
public void test() throws Exception {
ObjectMapper om = new ObjectMapper();
om.registerModule(new CustomModule());
String s = "{\"50000\":{\"testString\":{\"1\":10}},\"50001\":{\"testString1\":{\"2\":11}} }";
CustomData cd = om.readValue(s, CustomData.class);
System.out.println(cd.getData());
}
public static class CustomData {
private TLongObjectMap<Map<String, TIntIntMap>> data;
public CustomData() {
this.data = new TLongObjectHashMap<>();
}
public TLongObjectMap<Map<String, TIntIntMap>> getData() { return data; }
public void setData(TLongObjectMap<Map<String, TIntIntMap>> data) { this.data = data; }
}
public static class CustomModule extends SimpleModule {
public CustomModule() {
addSerializer(CustomData.class, new CustomSerializer());
addDeserializer(CustomData.class, new CustomDeserializer());
}
public static class CustomSerializer extends JsonSerializer<CustomData> {
#Override
public void serialize(CustomData value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
// add custom serializer here
}
}
public static class CustomDeserializer extends JsonDeserializer<CustomData> {
#Override
public CustomData deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
TLongObjectMap<Map<String, TIntIntMap>> data = new TLongObjectHashMap<>();
ObjectNode node = jsonParser.getCodec().readTree(jsonParser);
Iterator<Map.Entry<String,JsonNode>> fields = node.fields();
while (fields.hasNext()) {
Map.Entry<String, JsonNode> entry = fields.next();
ObjectNode value = (ObjectNode) entry.getValue();
Map.Entry<String, JsonNode> innerField = value.fields().next();
ObjectNode innerNode = (ObjectNode) innerField.getValue();
Map.Entry<String, JsonNode> innerInnerField = innerNode.fields().next();
TIntIntMap intMap = new TIntIntHashMap();
intMap.put(Integer.parseInt(innerInnerField.getKey()), innerInnerField.getValue().asInt());
Map<String, TIntIntMap> innerMap = Collections.singletonMap(innerField.getKey(), intMap);
data.put(Long.parseLong(entry.getKey()), innerMap);
}
CustomData customData = new CustomData();
customData.setData(data);
return customData;
}
}
}
}