JMockit mock private method in #PostConstruct - java

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

Related

Junit Test case for applicationContext.getBean(class) returning null

When trying to write test case for applicationContext.getBean(classname). Getting null pointer exception.
Below is the Java class
#Service
#Slf4j
public class MyClassName {
#Autowired
ServiceOne serviceOne;
#Autowired
ApplicationContext applicationContext;
public getCitizenData(String mobileNumber) {
CitizenDataService citizenDataService = applicationContext.getBean(CitizenDataService.class, mobileNumber);
log.info("Getting Data");
return citizenDataService.searchMethod(mobileNumber)
// While debugging test file citizenDataService is coming as Null Hence getting Null Pointer exception
.flatMap(.............
)
Test File
class MyClassNameTest {
private MyClassName mockMyClassName;
#BeforeEach
void setUp() {
mockMyClassName = new MyClassName();
mockMyClassName.serviceOne = mock(ServiceOne.class);
mockMyClassName.applicationContext = mock(ApplicationContext.class);
//mockMyClassName.applicationContext.getAutowireCapableBeanFactory();
}
#Test
void testGetCitizenData() {
// Setup
// Configure ApplicationContext.getBean(...).
final CitizenDataService citizenDataService = new CitizenDataService("mobileNumber");
when(mockMyClassName.applicationContext.getBean(CitizenDataService.class, "args"))
.thenReturn(citizenDataService);
final result = mockMyClassName.getCitizenData("mobileNumber");
// While debugging this citizenDataService is coming as Null Hence getting Null Pointer exception
How to write test case for this ?
It is because you stub the incorrect arguments on the getBean() on the mocked ApplicationContext . When a method on a mock is called but there are no matching stubbed arguments , it will return either null or an empty collection , or 0 for an int/Integer and false for a boolean/Boolean by default . So in you case NULL CitizenDataService is returned.
Changing the following should fix your problem :
when(mockMyClassName.applicationContext.getBean(CitizenDataService.class, "mobileNumber"))
.thenReturn(citizenDataService);
Another way is not to mock the ApplicationContext but use spring test to really start up the spring container which include the beans there are required for the test case :
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = {MyClassName.class,ServiceOne.class,CitizenDataService.class})
public class MyClassNameTest {
#Autowired
private MyClassName myClassName
#Test
void testGetCitizenData() {
}
}
This will be slower than the plain Mockito approach because it needs more time to start up the spring container and also require you to manage what beans to be included in the context.
For me , I would refactor your class such that it does not require to autowire ApplicationContext into it and then write a plain Mockito test. It is not a good practise to get the bean from the ApplicationContext manually which will make your class coupled to the spring framework codes.
I don't recommend mocking the application context. There are simply too many methods with similar arguments that it is difficult to mock the correct ones. Instead, use one of the readily available implementations written specifically for the use in tests, such as StaticApplicationContext.
class MyClassNameTest {
private MyClassName mockMyClassName;
private ApplicationContext appCtx;
#BeforeEach
void setUp() {
mockMyClassName = new MyClassName();
mockMyClassName.serviceOne = mock(ServiceOne.class);
this.appCtx = new StaticApplicationContext();
}
#Test
void testGetCitizenData() {
appctx.registerBean(CitizenDataService.class, () -> new CitizenDataService("mobileNumber"));
final result = mockMyClassName.getCitizenData("mobileNumber");

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

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

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.

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