Java Unittest Mocked Unittest Not working - java

I'm trying to create a unittest for the method below (myHostClient), but I'm having some problems with it:
MyClass.java
import com.abc.def.ServiceBuilder
public class MyClass {
#Value("${materialLocation}")
private String materialValue
private static final SERVICEBUILDER = new ServiceBuilder()
#Bean public MyHostServiceClient myHostClient(
#Value(${qualifier_one}) final String qualiferOne,
#Value(${use_service}) final boolean useService) {
if(useService) {
return SERVICEBUILDER
.remote(MyHostServiceClient.class)
.withConfig(qualifierOne)
.withCall(new CallerAttach(Caller.retro(defaultStrategy())), // Error Line2 Here
new SigningVisitor(new CredentialsProvider(materialValue))),
call -> call.doSomeStuff(StuffObject.getStuffInstance()))
.makeClient();
}
#Bean DefaultStrategy<Object> defaultStrategy() {
final int valueA = 1;
final int valueB = 2;
return new DoSomeThingsBuilder()
.retry(valueA)
.doSomethingElse(valueB)
.create();
}
}
And here is my latest unsuccessful attempt at writing a unittest for it:
MyClassTest.java
import org.mockito.Mock
import static org.mockito.Mockito.times
public class MyClassTest {
#Mock
private SERVICEBUILDER serviceBuilder;
private MyClass myClass;
private String qualifierOne = "pass"
#BeforeEach
void setUp() {
myClass = new MyClass();
}
#Test
public void test_myHostClient() {
boolean useService = true;
final MyHostServiceClient result = myclass.myHostClient(qualifierOne, useService); // Error Line1 here
verify(serviceBuilder, times(1));
}
}
I have been trying to mock SERVICEBUILDER and verify that the mocked object is called one time but no luck so far. Right now I'm getting this error:
IllegalArgumentException: Material Name cannot be null
And it points to these lines in my code.
In the Test:
final MyHostServiceClient result = myclass.myHostClient(qualifierOne, useService);
Which points to this line in the module:
.withCall(new CallerAttach(Caller.retro(defaultStrategy())),
Anyone know how I can fix my unittest or write a working one from scratch?

I would say the design of MyClass is quite wrong because it looks like a Spring configuration but apparently it's not. If it is really supposed to be a configuration then I wouldn't even test it like this because it would rather be an integration test. Of course, even in integration tests you can mock dependencies. But the test itself would run differently and you would have to spin up a suitable Spring context, etc.
So given the above, I would rather make MyClass some sort of MyHostServiceClientFactory with removing all of the Spring annotations and then fix the following problems in your code.
SERVICEBUILDER is hardcoded.
SERVICEBUILDER is static final and its value is hardcoded into MyClass. You will not be able to reassign that field with the mocked version. It can still be final but not static then and it's better to use dependency injection here by passing the value through the MyClass constructor.
SERVICEBUILDER will still be not mocked even if you fix the above.
To really mock SERVICEBUILDER by using the #Mock annotation in the test you should enable Mockito annotations.
If you are using JUnit5 then you should annotate your test class like this:
#ExtendWith(MockitoExtension.class)
public class MyClassTest {
...
}
If you are stuck with JUnit4 then you should use another combination:
#RunWith(MockitoJUnitRunner.class)
public class MyClassTest {
...
}
Once you've done that the SERVICEBUILDER will be mocked but now you will have to configure the behaviour of that mock, like what is going to be returned by the SERVICEBUILDER methods. I can see 4 methods in total, namely remote, withConfig, withCall, and makeClient. You will have to do Mockito's when/thenReturn configurations.
MyClass.materialValue is null.
But even when your mock will be properly configured you will still encounter the original IllegalArgumentException: Material Name cannot be null. This is because MyClass.materialValue will still be null and looks like CredentialsProvider cannot accept that. As I can see, that field is supposed to be injected by Spring using the #Value annotation, but remember this class no longer contains anything from Spring. As in problem 1, you have to pass the value through the MyClass constructor.
Once all of these problems are solved you can introduce a thin Spring configuration like MyHostServiceClientConfiguration (or whatever name suits you) that would serve as a provider of necessary properties/dependencies for MyHostServiceClientFactory (existing MyClass) and then this factory can provide you with a MyHostServiceClient bean through a method like MyHostServiceClientConfiguration#myHostServiceClient annotated with #Bean.
Conceptually your MyHostServiceClientFactory will look like this:
public class MyHostServiceClientFactory {
private final String materialValue;
private final ServiceBuilder serviceBuilder;
public MyHostServiceClientFactory(String materialValue, ServiceBuilder serviceBuilder) {
this.materialValue = materialValue;
this.serviceBuilder = serviceBuilder;
}
public MyHostServiceClient myHostClient(String qualiferOne, boolean useService) {
if(useService) {
return serviceBuilder
.remote(MyHostServiceClient.class)
.withConfig(qualifierOne)
.withCall(new CallerAttach(Caller.retro(defaultStrategy())), // Error Line2 Here
new SigningVisitor(new CredentialsProvider(materialValue))),
call -> call.doSomeStuff(StuffObject.getStuffInstance()))
.makeClient();
}
// can also be injected as a dependency rather than being hardcoded
DefaultStrategy<Object> defaultStrategy() {
final int valueA = 1;
final int valueB = 2;
return new DoSomeThingsBuilder()
.retry(valueA)
.doSomethingElse(valueB)
.create();
}
}

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

org.powermock.api.mockito.ClassNotPreparedException when it's declared to be prepared

I don't understand why this is showing since my test definitely asked PowerMock to prepare for it.
#PrepareForTest({AmazonSQSClientBuilder.class})
#RunWith(PowerMockRunner.class)
#PowerMockIgnore({"javax.*", "org.apache.logging.*"})
class EncodingAdapterTest {
...
private AmazonSQSClientBuilder sqsBuilder;
private AmazonSQS sqsClient;
private AmazonSNS snsClient;
private SendMessageResult sqsResult;
private PublishResult snsResult;
#BeforeEach
void setUp() {
sqsClient = PowerMockito.mock(AmazonSQS.class);
PowerMockito.mockStatic(AmazonSQSClientBuilder.class);
AmazonSQSClientBuilder mockBuilder = PowerMockito.mock(AmazonSQSClientBuilder.class);
PowerMockito.when(AmazonSQSClientBuilder.standard()).thenReturn(mockBuilder);
sqsBuilder = PowerMockito.mock(AmazonSQSClientBuilder.class);
}
Although I have #PrepareForTest({AmazonSQSClientBuilder.class}) at the very start, it shows error for line PowerMockito.mockStatic(AmazonSQSClientBuilder.class);.
The error goes as follows:
[Ljava.lang.Object;#259661b4
The class com.amazonaws.services.sqs.AmazonSQSClientBuilder not prepared for test.
org.powermock.api.mockito.ClassNotPreparedException:
[Ljava.lang.Object;#259661b4
The class com.amazonaws.services.sqs.AmazonSQSClientBuilder not prepared for test.
at org.powermock.api.mockito.expectation.reporter.MockitoPowerMockReporter.classNotPrepared(MockitoPowerMockReporter.java:29)
at org.powermock.api.mockito.internal.mockcreation.MockTypeValidatorFactory$DefaultMockTypeValidator.validate(MockTypeValidatorFactory.java:37)
at org.powermock.api.mockito.internal.mockcreation.AbstractMockCreator.validateType(AbstractMockCreator.java:10)
at org.powermock.api.mockito.internal.mockcreation.DefaultMockCreator.createMock(DefaultMockCreator.java:49)
at org.powermock.api.mockito.internal.mockcreation.DefaultMockCreator.mock(DefaultMockCreator.java:40)
at org.powermock.api.mockito.PowerMockito.mockStatic(PowerMockito.java:62)
at package.adapters.EncodingAdapterTest.setUp(EncodingAdapterTest.java:76)
Am I missing anything??
You could try extending the PowerMockTestCase. It would solve your problem.
class EncodingAdapterTest extends PowerMockTestCase
By the way, I recommend using mockito-inline instead of Powermock to mock static method or class. Because it is easier and more popular than Powermock. You can mock static easily, without superfluous annotations, like this:
try (MockedStatic<AmazonSQSClientBuilder> builder = Mockito.mockStatic(AmazonSQSClientBuilder.class)) {
builder.when(() -> AmazonSQSClientBuilder.standard())
.thenReturn("somthing");
}
Reference: https://www.baeldung.com/mockito-mock-static-methods

Mockito: Mock private field initialization

How I can mock a field variable which is being initialized inline?
class Test {
private Person person = new Person();
...
public void testMethod() {
person.someMethod();
...
}
}
Here I want to mock person.someMethod() while testing the Test.testMethod() method for which I need to mock initialization of person variable. Any clue?
Edit: I'm not allowed to modify Person class.
Mockito comes with a helper class to save you some reflection boiler plate code:
import org.mockito.internal.util.reflection.Whitebox;
//...
#Mock
private Person mockedPerson;
private Test underTest;
// ...
#Test
public void testMethod() {
Whitebox.setInternalState(underTest, "person", mockedPerson);
// ...
}
Update:
Unfortunately the mockito team decided to remove the class in Mockito 2. So you are back to writing your own reflection boilerplate code, use another library (e.g. Apache Commons Lang), or simply pilfer the Whitebox class (it is MIT licensed).
Update 2:
JUnit 5 comes with its own ReflectionSupport and AnnotationSupport classes that might be useful and save you from pulling in yet another library.
In case you use Spring Test try org.springframework.test.util.ReflectionTestUtils
ReflectionTestUtils.setField(testObject, "person", mockedPerson);
Pretty late to the party, but I was struck here and got help from a friend. The thing was not to use PowerMock. This works with the latest version of Mockito.
Mockito comes with this org.mockito.internal.util.reflection.FieldSetter.
What it basically does is helps you modify private fields using reflection.
This is how you use it:
#Mock
private Person mockedPerson;
private Test underTest;
// ...
#Test
public void testMethod() {
FieldSetter.setField(underTest, underTest.getClass().getDeclaredField("person"), mockedPerson);
// ...
verify(mockedPerson).someMethod();
}
This way you can pass a mock object and then verify it later.
Here is the reference.
I already found the solution to this problem which I forgot to post here.
#RunWith(PowerMockRunner.class)
#PrepareForTest({ Test.class })
public class SampleTest {
#Mock
Person person;
#Test
public void testPrintName() throws Exception {
PowerMockito.whenNew(Person.class).withNoArguments().thenReturn(person);
Test test= new Test();
test.testMethod();
}
}
Key points to this solution are:
Running my test cases with PowerMockRunner: #RunWith(PowerMockRunner.class)
Instruct Powermock to prepare Test.class for manipulation of private fields: #PrepareForTest({ Test.class })
And finally mock the constructor for Person class:
PowerMockito.mockStatic(Person.class);
PowerMockito.whenNew(Person.class).withNoArguments().thenReturn(person);
Following code can be used to initialize mapper in REST client mock. The mapper field is private and needs to be set during unit test setup.
import org.mockito.internal.util.reflection.FieldSetter;
new FieldSetter(client, Client.class.getDeclaredField("mapper")).set(new Mapper());
if u are using spring boot test and cant find neither of WhiteBox, FeildSetter; u can simply use org.springframework.test.util.ReflectionTestUtils
this is an example:
import org.springframework.test.util.ReflectionTestUtils;
//...
#Mock
private Person mockedPerson;
private Test underTest;
// ...
#Test
public void testMethod() {
ReflectionTestUtils.setField(underTestObject, "person", mockedPerson);
// ...
}
Using #Jarda's guide you can define this if you need to set the variable the same value for all tests:
#Before
public void setClientMapper() throws NoSuchFieldException, SecurityException{
FieldSetter.setField(client, client.getClass().getDeclaredField("mapper"), new Mapper());
}
But beware that setting private values to be different should be handled with care. If they are private are for some reason.
Example, I use it, for example, to change the wait time of a sleep in the unit tests. In real examples I want to sleep for 10 seconds but in unit-test I'm satisfied if it's immediate. In integration tests you should test the real value.
The best way until now, I think that is
org.springframework.test.util.ReflectionTestUtils
Im about to mock the private String mockField in FooService.class inside FooServiceTest.java
FooService.java:
#Value("${url.image.latest}")
private String latestImageUrl;
FooServiceTest.java:
#InjectMocks
FooService service;
#BeforeEach
void setUp() {
ReflectionTestUtils.setField(service, // inject into this object
"latestImageUrl", // assign to this field
"your value here"); // object to be injected
}
commons-lang3
import org.apache.commons.lang3.reflect.FieldUtils;
FieldUtils.writeField(object, fieldName, value, true);

Mockito - Desired returned mock object not being used in class-under-test (CUT)

I'm trying to get a handle on Mockito and have a situation where I'd like to use a mock object from within the class-under-test (CUT) but it does not appear to be working. I'm pretty sure I'm approaching the solution incorrectly. Here's some code:
CUT:
public class TestClassFacade {
// injected via Spring
private InterfaceBP bpService;
public void setBpService(InterfaceBP bpService) {
this.bpService = bpService;
}
public TestVO getTestData(String testString) throws Exception {
bpService = BPFactory.getSpecificBP();
BPRequestVO bpRequestVO = new BPRequestVO();
InterfaceBPServiceResponse serviceResponse = bpService.getProduct(bpRequestVO);
if (serviceResponse.getMessage().equalsIgnoreCase("BOB")) {
throw new Exception();
} else {
TestVO testVO = new TestVO();
}
return testVO;
}
}
Spring Configuration:
<bean id="testClass" class="com.foo.TestClassFacade">
<property name="bpService" ref="bpService" />
</bean>
<bean id="bpService" class="class.cloud.BPService" />
Mockito Test Method:
#RunWith(MockitoJUnitRunner.class)
public class BaseTest {
#Mock BPService mockBPService;
#InjectMocks TestClassFacade mockTestClassFacade;
String testString = "ThisIsATestString";
BPRequestVO someBPRequestVO = new BPRequestVO();
InterfaceBPServiceResponse invalidServiceResponse = new BPServiceResponse();
#Test (expected = Exception.class)
public void getBPData_bobStatusCode_shouldThrowException() throws Exception {
invalidServiceResponse.setMessage("BOB");
when(mockBPService.getProduct(someBPRequestVO)).thenReturn(invalidServiceResponse);
mockTestClassFacade.getTestData(testString);
verify(mockBPService.getProduct(someBPRequestVO));
}
}
What I'm trying to do is verify that the "if" conditional portion of the CUT (throwing the Exception) is being invoked in the case of a "BOB" message string being returned from the response of the 3rd party class (BPService). However, what's happening is that the "invalidResponse" object that I am trying to return in the "when" statement above isn't actually being returned when I run my mockTestClassFacade in the line below it. Instead, the
InterfaceBPServiceResponse serviceResponse = bpService.getProduct(bpRequestVO);
line in the real method is being invoked and the "serviceResponse" is being used during my test.
How do I get my mockTestClassFacade to use my "invalidServiceResponse" in this situation?
Thanks a lot...if anything isn't clear please let me know!
I think the problem is in bpService = BPFactory.getSpecificBP();.
You're mocking and injecting InterfaceBP into TestClassFacade, but inside the method getTestData you're creating a new InterfaceBP from BPFactory.
So when testing, you're not actually using the mock, but a different object.
If InterfaceBP is created and injected by Spring, you shouldn't need a factory to get an instance.
Continuing from the other answer, you need to mock the behavior of "BPFactory.getSpecificBP()", but Mockito won't let you mock static methods. You'll have to use PowerMock for this test.

Categories