Apache Beam , mock external Clients initialized in #Setup Lifecycle method of DoFn - java

I have created the following DoFn class, but i am not sure, how to mock APIClient, since creating the instance of JsonToGenericRecordMapper in Junit with mocked APICall object, is getting overridden with #setup method, which is creating actual instance of APICall ?
public class JsonToGenericRecordMapper extends DoFn<String, GenericRecord> {
private APICall apiCall;
#setup
public void setup ()
{
api = new APICall();
}
#ProcessElement
public void processElement(ProcessContext processContext) {
String enrichedItemCost = processContext.element();
Schema schema = CommonUtils.schema;
GenericRecord genericRecord = new GenericData.Record(schema);
genericRecord.put(CommonUtils.SCHEMA_FIELD_KEY, enrichedItemCost);
processContext.output(genericRecord);
}
}
TestPipeline testPipeline = TestPipeline.create();
APICAll apiCall = Mockito.mock(APICall.class);
dataPCollection.apply(new JsonToGenericRecordMapper(apiCall));

you can create two constructors (an empty and one one with the APICall parameter), and use one of them for testing, something like that (I just extended your example to get the point, so it's just an example):
public class JsonToGenericRecordMapper extends DoFn<String, GenericRecord> {
private APICall apiCall;
public JsonToGenericRecordMapper() {
}
public JsonToGenericRecordMapper(APICall apiCall) {
this.apiCall = apiCall;
}
#setup
public void setup ()
{
if(apiCall==null) {
api = new APICall();
}
}
#ProcessElement
public void processElement(ProcessContext processContext) {
String enrichedItemCost = processContext.element();
Schema schema = CommonUtils.schema;
GenericRecord genericRecord = new GenericData.Record(schema);
genericRecord.put(CommonUtils.SCHEMA_FIELD_KEY, enrichedItemCost);
processContext.output(genericRecord);
}
}
TestPipeline testPipeline = TestPipeline.create();
APICAll apiCall = Mockito.mock(APICall.class);
dataPCollection.apply(new JsonToGenericRecordMapper(apiCall));

Related

AssertionError when I try to write a unit test

I'm creating the processor to collect data and provide them in list. But when I'm trying to create test for my method i'm catching assertionerror. What am I doing wrong?
My Class:
#AllArgsConstructor
public class ZteProcessor implements OurProcessor {
private final static String VENDOR = "ZTE";
private String jsonString;
private Map<String, String> metricsGU;
private Map<String, String> metricsUMTS;
private Map<String, String> metricsLTE;
#Override
public List<TimingAdvance> getTA() throws ParseException, NotFoundPatternOrMetricsException {
TimeAdvanceDataStore data = new TimeAdvanceDataStore();
AllDataFromJSONFile fromJSONFile = ProcessorUtil.getAllData(jsonString);
if (jsonString.contains("String")) {
return data.allDataToTimingAdvance(VENDOR, fromJSONFile, metricsGU, 2);
} else if (jsonString.contains("String-2")) {
return data.allDataToTimingAdvance(VENDOR, fromJSONFile, metricsUMTS, 3);
} else if (jsonString.contains("String3")) {
return data.allDataToTimingAdvance(VENDOR, fromJSONFile, metricsLTE, 4);
} else {
throw new NotFoundPatternOrMetricsException();
}
}
}
My Test:
#RunWith(PowerMockRunner.class)
#PrepareForTest({ProcessorUtil.class})
public class ZteProcessorTest {
#Mock
private AllDataFromJSONFile fromJSONFile;
#Mock
private TimeAdvanceDataStore data;
private OurProcessor processor;
private TimingAdvance timingAdvance = new TimingAdvance();
private Map<String, String> metricsGU = new HashMap<>();
private Map<String, String> metricsUMTS = new HashMap<>();
private Map<String, String> metricsLTE = new HashMap<>();
#Test
public void getTATest() throws Exception {
String jsonString = " { String : value}";
processor = new ZteProcessor(jsonString, metricsGU, metricsUMTS, metricsLTE);
List<TimingAdvance> list = new ArrayList<>();
list.add(timingAdvance);
PowerMockito.mockStatic(ProcessorUtil.class);
when(ProcessorUtil.getAllData(jsonString)).thenReturn(fromJSONFile);
when(data.allDataToTimingAdvance(jsonString, fromJSONFile, metricsGU, 2)).thenReturn(list);
assertEquals(list, processor.getTA());
}
}
Stacktrace:
java.lang.AssertionError:
Expected :[TimingAdvance{filial='null', vendor='null', cellName='null', periodDate=null, taMetrics=null}]
Actual :[]
<Click to see difference>
at org.junit.Assert.fail(Assert.java:88)
at org.junit.Assert.failNotEquals(Assert.java:834)
at org.junit.Assert.assertEquals(Assert.java:118)
at org.junit.Assert.assertEquals(Assert.java:144)
My ZteProcessor using static method getAllData(jsonString) of class ProcessorUtill. And for it I use powermock.
The problem stems from the fact that you are setting your expectations on a TimeAdvanceDataStore data mock, but you are creating a new instance of TimeAdvanceDataStore in your method under test.
Since you are already using PowerMockito, you can tap into new object creation like
PowerMockito.whenNew(TimeAdvanceDataStore.class)
.withAnyArguments().thenReturn(data);
On top of that, think how many ZTEProcessors and TimeAdvanceDataStores you have in your app. Do you always want a new instance of TimeAdvanceDataStore in each call to getTA?
If not, just pass TimeAdvanceDataStore in a constructor.
If yes, common approaches when PowerMockito is not at your disposal are:
passing a factory of TimeAdvanceDataStore to ZTEProcessor constructor
passing TimeAdvanceDataStore to getTA method
extracting a method constructing TimeAdvanceDataStore and overriding it in test

Consume a Kafka message using a different model object than the one used at the producer

My application is a Kafka consumer which receives a big fat custom message from the producer.
We use Jackson to serialize and deserialize the messages.
A dummy of my consumer is here.
public class LittleCuteConsumer {
#KafkaListener(topics = "${kafka.bigfat.topic}", containerFactory = “littleCuteConsumerFactory")
public void receive(BigFatMessage message) {
// do cute stuff
}
}
And the message that's been transferred
#JsonIgnoreProperties(ignoreUnknown = true)
public class BigFatMessage {
private String fieldOne;
private String fieldTwo;
...
private String fieldTen;
private CustomeFieldOne cf1;
...
private CustomeFieldTen cf10;
// setters and getters
}
Here is the object I want to deserialize the original message to.
#JsonIgnoreProperties(ignoreUnknown = true)
public class ThinMessage {
private String fieldOne;
private String fieldTwo;
// setters and getters
}
Original deserializer
public class BigFatDeserializer implements Deserializer<BigFatMessage> {
#Override
public void configure(Map<String, ?> configs, boolean isKey) {
// Default implementation of configure method
}
#Override
public BigFatMessage deserialize(String topic, byte[] data) {
ObjectMapper mapper = new ObjectMapper();
BigFatMessage biggie = null;
try {
biggie = mapper.readValue(data, BigFatMessage.class);
} catch (Exception e) {
// blame others
}
return biggie;
}
#Override
public void close() {
// Default implementation of close method
}
}
As we can see here, the message contains a lot of fields and dependent objects which are actually useless for my consumer, and I don't want to define all the dependent classes in my consumer as well.
Hence, I need a way I to receive the message using a simple different model class and deserialize it to ignore the unnecessary fields from the original message!
How I'm trying to deserialize
public class ThinDeserializer implements Deserializer<ThinMessage> {
#Override
public void configure(Map<String, ?> configs, boolean isKey) {
// Default implementation of configure method
}
#Override
public ThinMessage deserialize(String topic, byte[] data) {
ObjectMapper mapper = new ObjectMapper();
ThinMessage cutie = null;
try {
cutie = mapper.readValue(data, ThinMessage.class);
} catch (Exception e) {
// blame others
}
return cutie;
}
#Override
public void close() {
// Default implementation of close method
}
}
And get the below Jackson error:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of com.myapp.ThinMessage (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)\n
Accompanied by below Kafka exception.
org.springframework.kafka.listener.ListenerExecutionFailedException: Listener method could not be invoked with the incoming message\n
org.springframework.messaging.handler.annotation.support.MethodArgumentNotValidException: Could not resolve method parameter at index 0
Try to change
public class ThinMessage {
private String fieldOne;
private String fieldTwo;
}
to
#JsonIgnoreProperties(ignoreUnknown = true)
public class ThinMessage {
private String fieldOne;
private String fieldTwo;
public ThinMessage() {
}
public String getFieldOne() {
return fieldOne;
}
public void setFieldOne(String fieldOne) {
this.fieldOne = fieldOne;
}
public String getFieldTwo() {
return fieldTwo;
}
public void setFieldTwo(String fieldTwo) {
this.fieldTwo = fieldTwo;
}
}
and set
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
check this link : (https://docs.spring.io/spring-kafka/docs/2.3.x/reference/html/#json)
you have two options : remove typeInfo from producer or ingnore typeInfo from consumer
#Bean
public DefaultKafkaProducerFactory pf(KafkaProperties properties) {
Map<String, Object> props = properties.buildProducerProperties();
DefaultKafkaProducerFactory pf = new DefaultKafkaProducerFactory(props,
new JsonSerializer<>(MyKeyType.class)
.forKeys()
.noTypeInfo(),
new JsonSerializer<>(MyValueType.class)
.noTypeInfo());
}
#Bean
public DefaultKafkaConsumerFactory pf(KafkaProperties properties) {
Map<String, Object> props = properties.buildConsumerProperties();
DefaultKafkaConsumerFactory pf = new DefaultKafkaConsumerFactory(props,
new JsonDeserializer<>(MyKeyType.class)
.forKeys()
.ignoreTypeHeaders(),
new JsonSerializer<>(MyValueType.class)
.ignoreTypeHeaders());
}

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);
}

Convert List<T> to Collection<Object[]> for JUnit Parametrized Test

I would like to perform a JUnit Parametrized test with data being external.
I have a list of objects and just need to know how I can convert that to a collection of object arrays. I see the below stack overflow question, but I want to add data from a file which is read from my method.
Parameterized JUnit tests with non-primitive parameters?
Working code: something like this:
#RunWith(Parameterized.class)
public class sampletest {
private BranchMailChildSample branch;
public sampletest(BranchMailChildSample branch)
{
this.branch = branch;
}
#Parameters
public static Collection<Object[]> data()
{
String excel = "C:\\Resources\\TestData\\ExcelSheets\\BranchMail\\branchmail_TestData.xlsx";
ExcelMarshallerTool tool = new ExcelMarshallerTool(excel);
List<BranchMailChildSample> items = tool.unmarshallExcel(BranchMailChildSample.class);
//RIGHT HERE I NEED HELP: Convert list to Collection<Object[]>
//return items as Collection of object arrays
}
#Test
public void test()
{
System.out.println(branch.toString());
}
}
You don't have to convert the list.
#RunWith(Parameterized.class)
public class SampleTest {
#Parameters(name = "{0}")
public static List<BranchMailChildSample> data() {
String excel = "C:\\Resources\\TestData\\ExcelSheets\\BranchMail\\branchmail_TestData.xlsx";
ExcelMarshallerTool tool = new ExcelMarshallerTool(excel);
return tool.unmarshallExcel(BranchMailChildSample.class);
}
#Parameter(0)
public BranchMailChildSample branch;
#Test
public void test() {
System.out.println(branch.toString());
}
}
I used field injection, because it needs less code than constructor injection. Setting the name to the object under test prints more helpful output. Please have a look at the documentation of the Parameterized runner.
You can use constructor injection if you don't like the public field.
#RunWith(Parameterized.class)
public class SampleTest {
#Parameters(name = "{0}")
public static List<BranchMailChildSample> data() {
String excel = "C:\\Resources\\TestData\\ExcelSheets\\BranchMail\\branchmail_TestData.xlsx";
ExcelMarshallerTool tool = new ExcelMarshallerTool(excel);
return tool.unmarshallExcel(BranchMailChildSample.class);
}
private final BranchMailChildSample branch;
public SampleTest(BranchMailChildSample branch) {
this.branch = branch;
}
#Test
public void test() {
System.out.println(branch.toString());
}
}
That's why I would suggest you to use TestNG instead if possible.
It's work the same as JUnit but you can use a internal or external DataProvider, execute methods at a given order... It is much more convenient to use
//This method will provide data to any test method that declares that its Data Provider
//is named "test1"
#DataProvider(name = "test1")
public Object[][] createData1() {
return new Object[][] {
{ "Cedric", new Integer(36) },
{ "Anne", new Integer(37)},
};
}
//This test method declares that its data should be supplied by the Data Provider
//named "test1"
#Test(dataProvider = "test1")
public void verifyData1(String n1, Integer n2) {
System.out.println(n1 + " " + n2);
}
I got it to work doing this:
List<BranchMailChildSample> items = tool.unmarshallExcel(BranchMailChildSample.class);
Collection<Object[]> data = new ArrayList<Object[]>();
for(BranchMailChildSample item : items)
{
Object[] objItem = new Object[] { item };
data.add(objItem);
}
return data;
Actually, if you use JUnit 4.12 and the theories runner, something like this should just work:
#RunWith(Theories.class)
public class MyTest
{
#DataPoints public static List<MyClass> myFunctionThatReturnsTestData()
{
// TODO
}
#Theory
public void canDoTheThing(MyClass m) throws Exception {

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

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);

Categories

Resources