Can't access methods of #Injectable inside Expectations() of Jmockit? - java

I define an #Injectable in my test class as below
#Injectable
IndividualPaymentServiceLocal individualPaymentService;
and then initialize this reference inside #Before method of Junit as
individualPaymentService = new MockUp<IndividualPaymentServiceLocal>() {
#Mock
public void $init() {
}
#Mock
public List<IndividualPayment> search(#Nullable String clinicId, #NotNull TimeWindow timeWindow, #Nullable IndividualPaymentFetchConfig fetchConfig) {
return paymentsList_1;
}
}.getMockInstance();
IndividualPaymentServiceLocal is a local EJB interface and has a
search() method that I mock as shown above to return an ArrayList
paymentsList_1. Now in my one of #Test methods, I wish to return a
different ArrayList so I try to use Jmockit's Expectations like below
new Expectations(){
individualPaymentService.search(anyString,any,any); result=paymentsList_2;
};
but search method is not resolved on individualPaymentService reference so code doesn't compile. Outside Expectations, its resolved. Am I missing something? I am using IntelliJ Idea. Please suggest.

Related

How to mock a newly created object for AWSLambda/AWSStepFunctions that uses ClientBuilders?

I have a class that I'm trying to test where it creates a new object of type AWSStepFunctions, and I'm trying to mock it to return a mock of the same type. I cannot change this original line of code, I can only add tests, so I was wondering how I could go about mocking it.
The class looks looks this --
Class class{
public Object handleRequest(Object object, Context context) {
AWSStepFunctions client = AWSStepFunctionsClientBuilder.standard().withClientConfiguration(new ClientConfiguration()).build();
client.startExecution(...);
}
}
The testing code looks like this -
public class ClassTest {
#Test
public void testHandlerRequest() {
mockStatic(AWSStepFunctionsClientBuilder.class); //mockStatic() and when() from PowerMockito
AWSStepFunctions awsStepFunctionsMock = mock(AWSStepFunctions.class);
AWSStepFunctionsClientBuilder awsStepFunctionsClientBuilder = mock(AWSStepFunctionsClientBuilder.class);
ClientConfiguration configuration = mock(ClientConfiguration.class);
PowerMockito.whenNew(ClientConfiguration.class).withAnyArguments().thenReturn(awsStepFunctionsMock);
when(awsStepFunctionsClientBuilder.standard()).thenReturn(awsStepFunctionsClientBuilder);
when(awsStepFunctionsClientBuilder.withClientConfiguration()).thenReturn(awsStepFunctionsClientBuilder);
when(awsStepFunctionsClientBuilder.build()).thenReturn(awsStepFunctionsMock);
... more when-thenreturns
}
}
I'm running into errors such as NoSuchMethodError for the clientBuilder's mock.
I tried to use PowerMockito's whenNew to mock the creation of the new object of type AWSStepFunctions - PowerMockito.whenNew(AWSStepFunctions.class).withAnyArguments().thenReturn(awsStepFunctionsMock), but that doesn't seem to work as well. Is there a way to return this mock correctly?
You can directly mock static methods with Mockito and Junit5 without using Powermock.
ClassTest
#Test
void test() throws IOException {
try (MockedStatic<AWSStepFunctionsClientBuilder> awsMock = Mockito.mockStatic(AWSStepFunctionsClientBuilder.class, Mockito.RETURNS_DEEP_STUBS)) {
AWSStepFunctions awsStepFunctionsMock = mock(AWSStepFunctions.class);
// You can mock methods chaining when you specify Mockito.RETURNS_DEEP_STUBS
awsMock.when(() -> AWSStepFunctionsClientBuilder.standard().withClientConfiguration(Mockito.any()).build()).thenReturn(awsStepFunctionsMock);
}
}
You can read this post for more explanation about MockedStatic: https://www.baeldung.com/mockito-mock-static-methods
And this one about Mockito.RETURNS_DEEP_STUBS: https://www.baeldung.com/mockito-fluent-apis
Don't forget to configure Mockito to handle static mock :
test/resources/mockito-extensions/org.mockito.plugins.MockMaker
mock-maker-inline

Mocking a ListState in Apache Flink 1.4

I am writing some test code for a processElement function in Apache Flink 1.4:
public class ProcessFunctionClass {
public void processElement(Tuple2<String, String> tuple2, Context context, Collector<Tuple2<String, String>> collector) {
// if the state is empty, start a timer
if (listState.get().iterator().hasNext() == false)
context.timerService().registerEventTimeTimer(1000);
listState.add("someStringToBeStored");
// ...
}
}
public class ProcessFunctionClassTest {
private ProcessFunctionClass processFunctionClass;
#Mock
private ListState<String> listState;
#Before
public void setUp() throws Exception {
processFunctionClass = new ProcessFunctionClass();
}
#Test
public void testProcessElement() {
ListState mockListState = mock(ListState.class);
Iterable mockIterable = mock(Iterable.class);
Iterator mockIterator = mock(Iterator.class);
MockitoAnnotations.initMocks(this);
when(tDPListState.get()).thenReturn(mockIterable);
when(tDPListState.get().iterator()).thenReturn(mockIterator);
when(tDPListState.get().iterator().hasNext()).thenReturn(false);
processFunctionClass.processElement(tuple2, context, collector);
// verify(...)
}
}
When I debug using my IDE, just before I step into the processElement() method, listState is not null and appears to have been mocked successfully, but as soon as I get to listState.get().iterator().hasNext(), listState is null and I get a NullPointerException. What am I doing wrong here?
In ProcessFunctionClass you have a private listState variable.
In your test you create a completely unrelated mockListState variable and set some expectations on it.
For your test to work, you must provide a way (constructor or setter) to set ProcessFunctionClass.listState to desired value (your mocked list state)
On top of that, MockitoAnnotations.initMocks(this); seems to do nothing in your example: you haven't shown us any fields annotated with #Mock or #InjectMocks
Update
You are misusing #Mock annotation.
You should place it in the test class, not in class under test.
When placed in the test class, after a call to initMocks, the filed will be initialized with a mock of an appropriate type.
What you should fo instead:
remove MockitoAnnotations.initMocks(this);, you are creating all the mocks manually.
add a constructor in ProcessFunctionClass
public ProcessFunctionClass(ListState<String> listState) {
this.listState = listState
}
use this constructor in your test
var mockListState = mock(ListState.class);
var processFunctionClass = new ProcessFunctionClass();

How to stub a method of an class annotated with #InjectMocks?

Below MyDictionary.get method uses the injected map by calling map.get.
Just of curiosity I stubbed the MyDictionary.get method, like I always do with mocks and spies, so I overwrite the injection.
But this only works when MyDictionary.get indeed calls map.get. If map.get returns some string (the empty string here), the stub Mockito.when does not work. The behavior is as if it is not there. In the assert line, dictionary.get("key") equals the empty string. This is what I do not understand.
#RunWith(MockitoJUnitRunner.class)
public class MockitoTest {
#Mock
Map<String, String>map;
#InjectMocks
MyDictionary dictionary;
#Test
public void testMyDictionary(){
Mockito.when(dictionary.get("key")).thenReturn("value");
Assert.assertEquals("value", dictionary.get("key"));
}
private static class MyDictionary{
private Map<String, String> map;
public String get(String key){
return map.get(key);
// or,
return "";
}
}
}
I am really surprised that you do not get a MissingMethodInvocationException which is thrown when you try to stub a method of an object not being a #Mock or a #Spy.
The dictionary instance is just a regular instance of a class here not proxied by Mockito (because of the fact that only #InjectMocks annotation is used).
Another surprise is that you do not get a null when the map.get is triggered as default return value for a String returning method is null.
Anyway..
If you want to stub methods of the `dictionary' instance you have to configure your test class as follows:
#InjectMocks
#Spy
MyDictionary dictionary;
#Test
public void testMyDictionary(){
doReturn("value").when(dictionary).get("key);
Assert.assertEquals("value", dictionary.get("key"));
}

JMockit mock private method in #PostConstruct

Context
In my class under test there is a #PostConstruct annotated method that invokes another private method.
#PostConstruct
public void init() {
longWork(); // private method
}
JMockit's default behaviour is to execute #PostConstruct methods of #Tested classes upon injection.
If a #Tested class has a method annotated with
javax.annotations.PostConstruct, it should be executed right after
injection.
https://github.com/jmockit/jmockit1/issues/13
Problem
JMockit calls the init() method, what I don't want.
From the thread dump:
at com.me.Worker.longWork(Worker.java:56)
at com.me.Worker.longWork.init(Worker.java:47)
...
at mockit.internal.util.MethodReflection.invoke(MethodReflection.java:96)
How can that call be mocked/removed/blocked?
Attempts
I tried to mock the init and longWork methods as shown below. However that results in NullPointerException as sut is not injected yet.
#Before
public void recordExpectationsForPostConstruct()
{
new NonStrictExpectations(sut) {{ invoke(sut, "init"); }};
}
You can try manually initializing your class to be tested without using #Tested. And then, set mock dependencies via setter methods (or using mockit.Deencapsulation.setField() method).
You can try something similar to this;
//Define your class under test without any annotation
private MyServiceImpl serviceToBeTested;
//Create mock dependencies here using #Mocked like below
#Mocked Dependency mockInstance;
#Before
public void setup() {
//Initialize your class under test here (or you can do it while declaring it also).
serviceToBeTested = new MyServiceImpl();
//Set dependencies via setter (or using mockit.Deencapsulation.setField() method)
serviceToBeTested.setMyDependency(mockInstance);
//Optionally add your expectations on mock instances which are common for all tests
new Expectations() {{
mockInstance.getName(); result = "Test Name";
...
}};
}
#Test
public void testMyMethod(#Mocked AnotherDependency anotherMock) {
//Add your expectations on mock instances specifics to this test method.
new Expectations() {{
mockInstance.getStatus(); result = "OK";
anotherMock.longWork(); result = true;
...
}};
//Set dependencies via setter or using mockit.Deencapsulation.setField() method
Deencapsulation.setField(serviceToBeTested, "myAnotherDep", anotherMock);
//Invoke the desired method to be tested
serviceToBeTested.myMethod();
//Do the verifications & assertions
new Verifications() {{
....
....
}};
}
Perhaps you could delegate the longWork method to a different class and mock this one. Difficulties while writing tests is often a sign of design flaws

Easymock andReturn(a mock) returns null

Using EasyMock 3.2, I have a test that is essentially the same as the one I have below. When I run this test there is a null pointer exception when the SUT tries to do daoSupport.getHibernateTemplate().loadAll(); When the mocked daoSupport is supposed to return the mocked template, it returns null.
#RunWith(EasyMockRunner.class)
public class DAOImplTest extends EasyMockSupport {
#Mock
private HibernateDaoSupport daoSupport;
#Mock
private HibernateTemplate template;
#Test
public void test() {
expect(daoSupport.getHibernateTemplate()).andReturn(template).once(); //1
expect(template.loadAll()).andReturn(Collections.emptyList()).once(); //2
replayAll();
SUT mySUT = new SUT(daoSupport);
mySUT.exercise();
verifyAll();
}
}
I can get the test to work by replacing //1 in the snippet above here with
daoSupport.setHibernateTemplate(template);
Obviously this is not what I want to do. I want the mocked daoSupport to return the mocked template. What is wrong here?
The reason, as discribed in the EasyMock documentation:
Final methods cannot be mocked. If called, their normal code will be executed.
It just so happens that HibernateDaoSupport#getHibernateTemplate() is final. Since I can not change the method signature, the best I can do is to extract an interface for this class. Alternatively I can use Powermock, as mentioned in this answer.
At the end of the day, executing the normal code of the getter is not so bad. It's just a getter.

Categories