I am facing some difficulty in writing a JUnit test for a method with a While loop. My method looks like below:
private void deleteMethod(DeleteRequest dr){
// below statement am calling some service which returns me a object after querying it from Database.
SomeObject ob = db.getdata(dr);
while(ob != null) {
// this method deletes the Data from DB
db.deleteData(ob);
// again calling the same service operation as we did before while loop. I have a situation where my service only returns single record at a time. It is avoidable that I need to do a dirty job.
ob = db.getdata(dr);
}
Below is my JUnit:
#Test
public void testDeleteMethod() throws Exception{
DeleteRequest mockDR = new DeleteRequest();
mockDR.setX(y);
SomeObject mockOB = new SomeObject();
mockOB.setZ(k);
// making a mockcall to the method before I can assert anything
mockClassObject.deleteMethod(mockDR);
}
The above JUnit method is getting struck in execution for ever and I understand that its not getting out of the while loop. How I can approach this issue. Just to mention am using Mockito framework and am not aware if at all there is any way to handle this situation in Mockito.
Using Mockito, you have to mock the db connection and inject it into your service using #InjectMocks, constructor injection, or via a setter before you test your method. This is how I would write your test.
#Test
public void testDeleteMethod() throws Exception{
DeleteRequest deleteRequest = new DeleteRequest();
deleteRequest.setX(y);
SomeObject someObject = new SomeObject();
someObject.setZ(k);
Database db = Mockito.mock(Database.class);
// Notice chain of calls
Mockito.when(db.getdata(deleteRequest))
.thenReturn(someObject).thenReturn(null);
// TODO Inject the Database object into your mockClassObject.
// making a mock call to the method before I can assert anything
mockClassObject.deleteMethod(deleteRequest);
Mockito.verify(db, Mockito.times(2)).getdata(deleteRequest);
Mockito.verify(db).deleteData(someObject);
}
You can see that I chain the calls on the db.getData() method two times, the first time returns someObject and the second time returns null.
Related
I've got a class that is wrapping objects with a proxy using Groovy's metaClass "getProperty" and "invokeMethod" methods. The proxy/wrapper lets me do lazy loading on the objects, only grab the data once the getter for a property has been called.
It works well when I actually use the getters, but I ran into a problem when I started trying to use Jackson to serialize the object (a secondary use case). I thought that Jackson would spin through all of the getter methods, which would invoke each of the getters and load all the data. But unlike when I call getters myself, when Jackson uses reflection to call the methods, my interceptor methods are not invoked, so all my data is still null.
I trace the call to my object in the Jackson library to this bit of code in the BeanPropertyWriter. The _accessorMethod.invoke(bean) call invokes the 'getter' method, but not the code in my proxy. I was able to recreate it in a unit test too outside of Jackson too.
Is there something I should implement in my Groovy proxy class that would capture those requests via the Method objects in reflection?
This is what my proxying code looks like:
static <T> T lazyProxyFor(T object) {
Class clazz = object.class
object.metaClass.getProperty = { String name ->
println "getting property $name"
def currentVal = clazz.metaClass.getMetaProperty(name).getProperty(delegate)
if (!currentVal) {
setProperty('testing') //normally this goes and gets data from my service
}
return currentVal
}
object.metaClass.invokeMethod = { String name, Object args ->
println "calling $name"
if (name ==~ /get[A-Z][a-zA-Z0-9_]*/ && args?.size() == 0) {
object[name.drop(3).uncapitalize()] // call the getProperty method
} else {
clazz.metaClass.getMetaMethod(name, args)?.invoke(delegate, args) //like the underlying object process the
}
}
return object
}
and this is the code I was testing with:
Address address = lazyProxyFor(Address)
address.class.declaredMethods.each{ method ->
if (method.name.startsWith('get')) {
method.invoke(address) //<-- if I run with this version I get no output
//address."$method.name"() //<-- if I run with this version I get two messages, one for each
//getter method and then the underlying property, presummably because
//groovy is more involved here, instead of reflection
}
}
For mocking local variable/method call on local object that is constructed inside method under test, we are using PowerMockito library as of now.
We are trying to evaluate whether we can use mockito-inline (version 3.7.7) to do the same.
In short, we are trying to intercept construction of object using Mockito.mockConstruction, so that we can specify mocked behavior on object created locally.
Here is a scenario which describes our usage. Since this is legacy code, we are not in a position to change it now.(e.g. Change dependency to instance variable or some other refactoring)
In short, execute() method of class MyClass is constructing object of Util class locally. Since we want to unit test core logic of execute() & hence need to mock process() method called on local Util object created inside execute() method of class MyClass.
public class MyClass {
public String execute(){
Util localUtil = new Util();
String data = localUtil.process();
//Core logic of the execute() method that needs to be unit tested...
//Further update data based on logic define in process() method.
data = data + " core logic";
return data;
}
}
public class Util {
public String process(){
String result = "real";
//Use various other objects to arrive at result
return result;
}
}
#Test
public void testLocalVar(){
Util util = new Util();
Assertions.assertEquals("real", util.process());
try (MockedConstruction<Util> mockedConstruction = Mockito.mockConstruction(Util.class);) {
util = new Util();
when(util.process()).thenReturn("mocked method data");
String actualData = util.process();
//This works on local object created here.
Assertions.assertEquals("mocked method data",actualData);
//Object,method under test
MyClass myClass = new MyClass();
actualData = myClass.execute();
//This doesn't not works on local object created inside method under test
Assertions.assertEquals("mocked method data" + " core logic",actualData); //Fails
}
}
Here we are able to define mocked behavior on method on object created locally inside test method. But same thing is not possible with local object created inside actual method under test.
I can see mockedConstruction.constructed() does have every object of class created
But not able to specify mocked behavior on on localUtil.process() inside execute() method ..
Any suggestions..
In your case, you have to stub the behavior when instructing Mockito to mock the constructor. The Mockito.mockConstruction() is overloaded and allows to not only pass the class you want to mock the constructor for:
#Test
void mockObjectConstruction() {
try (MockedConstruction<Util> mocked = Mockito.mockConstruction(Util.class,
(mock, context) -> {
// further stubbings ...
when(mock.process()).thenReturn("mocked method data");
})) {
MyClass myClass = new MyClass();
actualData = myClass.execute();
}
}
You can find further possible stubbing scenarios in this article.
I am trying to mock an external call along with an ArgumentMatcher to match the input values to the request. But when I trying to fetch the map from the ArgumentMatcher object, it gives me a null value.
Mockito.when(
dynamoDbMapper.scanPage(eq(ABC.class), argThat(new ArgumentMatcher<DynamoDBScanExpression>() {
#Override
public boolean matches(Object argument)
{
DynamoDBScanExpression scanExp = (DynamoDBScanExpression) argument;
Assert.assertEquals("5", scanExp.getLimit());
Assert.assertEquals("xyz",scanExp.getFilterExpression());
Assert.assertEquals(new HashMap(), scanExp.getExpressionAttributeNames());
return true;
}
}))).thenReturn(prepareScanResponse());
This expression scanExp.getExpressionAttributeNames() should ideally return a map but gives me a null value.
So suppose I have to mock a request whose input contains a map, and then try to implement ArgumentMatcher on that inout object which contains a map as an attribute, how would I do that?
Why not use a #Captor? Captors are used to get record parameters passed to methods. It seems like a cleaner way than to try to misuse a matcher.
#ExtendWith(MockitoExtension.class)
class MarketplaceHttpConnectorImplTest {
#Captor
ArgumentCaptor<DynamoDBScanExpression> scanExpressionCaptor;
#Mock
DynamoMapper dynamoDbMapper; // or something like this
#InjectMocks
MyClassToTest sut; // System Under Test
#Test
public void myTest() {
// prepare mocks
when(dynamoDbMapper.scanPage(eq(ABC.class), any(DynamoDBScanExpression.class)).thenReturn(prepareScanResponse());
// Now call the method to test
sut.methodToCall();
// Verify calls
verify(dynamoDbMapper, times(1)).scanPage(eq(ABC.class), scanExpressionCaptor.capture());
DynamoDBScanExpression param = scanExpressionCaptor.getValue();
// now test what was passed to the method.
assertNotNull(param);
// .....
}
}
Btw: don't mind the JUnit5. It also works in JUnit4. Also, I presumed there was just one value. You can capture multiple values in one #Captor and check all values.
In this code, i have mocked the one of the ValidateHandlerSoapClient class method which is instantiated and called this method (soapClientSpy.processSoapRequestRespons) in validateMsisdnHandlerIRSpy.validate().So soapClientSpy.processSoapRequestResponse is not working , instead the real method is called.
#RunWith(PowerMockRunner.class)
#PrepareForTest({ValidateMsisdnHandler.class,ValidateHandlerSoapClient.class})
public class Demo {
MessageControl messageControl=PowerMockito.mock(MessageControl.class);
Validate validate=PowerMockito.mock(Validate.class);
ValidateMsisdnHandlerIR validateMsisdnHandlerIRSpy = PowerMockito.spy(new ValidateMsisdnHandlerIR());
ValidateHandlerSoapClient soapClientSpy = PowerMockito.spy( new ValidateHandlerSoapClient());
#Before
public void initialize() throws Exception
{
PowerMockito.when(validate.getAccountId()).thenReturn("0879221485");
PowerMockito.doReturn(true).when(validateMsisdnHandlerIRSpy, "isPrePaid",anyString());
MemberModifier.field( ValidateMsisdnHandlerIR.class, "endDate").set(
validateMsisdnHandlerIRSpy, "10-FEB-2015");
PowerMockito.when(soapClientSpy.processSoapRequestResponse(anyString())).thenReturn(true);
PowerMockito.whenNew(ValidateHandlerSoapClient.class).withNoArguments().thenReturn(soapClientSpy);
}
#Test
public void testValidateMsisdn_Cr6_Roverprempay_Not_Roverpayg() throws Exception{
Response response = validateMsisdnHandlerIRSpy.validate(validate,messageControl);
}
Replace
ValidateHandlerSoapClient soapClientSpy = PowerMockito.spy( new ValidateHandlerSoapClient())
with
ValidateHandlerSoapClient soapClientMock = PowerMockito.mock(ValidateHandlerSoapClient.class)
A spy by default just calls the methods of the underlying regular class. What you want to do is (presumably) nothing when the methods of the soap client are called.
Then of course you will need to make also change:
PowerMockito.whenNew(ValidateHandlerSoapClient.class).withNoArguments().thenReturn(soapClientMock);
I am new to Mockito as a concept. Can you please help me understand using Mockito for formhandlers in ATG. Some examples will be appreciated.
There is a good answer (related to ATG) for other similar question: using-mockito-for-writing-atg-test-case. Please review if it includes what you need.
Many of ATG-specific components (and form handlers particularly) are known to be "less testable" (in comparison to components developed using TDD/BDD approach), b/c design of OOTB components (including reference application) doesn't always adhere to the principle of having "Low Coupling and High Cohesion"
But still the generic approach is applicable for writing unit-tests for all ATG components.
Below is a framework we've used for testing ATG FormHandlers with Mockito. Obviously you'll need to put in all the proper bits of the test but this should get you started.
public class AcmeFormHandlerTest {
#Spy #InjectMocks private AcmeFormHandler testObj;
#Mock private Validator<AcmeInterface> acmeValidatorMock;
#Mock private DynamoHttpServletRequest requestMock;
#Mock private DynamoHttpServletResponse responseMock;
private static final String ERROR1_KEY = "error1";
private static final String ERROR1_VALUE = "error1value";
#BeforeMethod(groups = { "unit" })
public void setUp() throws Exception {
testObj = new AcmeFormHandler();
initMocks(this);
}
//Test the happy path scenario
#Test(groups = { "unit" })
public void testWithValidData() throws Exception {
testObj.handleUpdate(requestMock, responseMock);
//Assume your formhandler calls a helper method, then ensure the helper method is called once. You verify the working of your helper method as you would do any Unit test
Mockito.verify(testObj).update(Matchers.refEq(requestMock), Matchers.refEq(responseMock), Mockito.anyString(), (AcmeBean) Mockito.anyObject());
}
//Test a validation exception
#Test(groups = { "unit" })
public void testWithInvalidData() throws Exception {
Map<String, String> validationMessages = new HashMap<String, String>();
validationMessages.put(ERROR1_KEY, ERROR1_VALUE);
when(acmeValidatorMock.validate((AcmeInterface) Mockito.any())).thenReturn(validationMessages);
testObj.handleUpdate(requestMock, responseMock);
assertEquals(1, testObj.getFormExceptions().size());
DropletFormException exception = (DropletFormException) testObj.getFormExceptions().get(0);
Assert.assertEquals(exception.getMessage(), ERROR1_VALUE);
}
//Test a runtime exception
#Test(groups = { "unit" })
public void testWithRunProcessException() throws Exception {
doThrow(new RunProcessException("")).when(testObj).update(Matchers.refEq(requestMock), Matchers.refEq(responseMock), Mockito.anyString(), (AcmeBean) Mockito.anyObject());
testObj.handleAddGiftCardToCart(requestMock, responseMock);
assertEquals(1, testObj.getFormExceptions().size());
DropletFormException exception = (DropletFormException) testObj.getFormExceptions().get(0);
Assert.assertEquals(exception.getMessage(), GENERAL_ERROR_KEY);
}
}
Obviously the above is just a framework that fit in nicely with the way in which we developed our FormHandlers. You can also add validation for redirects and stuff like that if you choose:
Mockito.verify(responseMock, Mockito.times(1)).sendLocalRedirect(SUCCESS_URL, requestMock);
Ultimately the caveats of testing other people's code still applies.
Here's what I do when I unit test a form handler (at least until I manage to release a major update for AtgDust). Note that I don't use wildcard imports, so I'm not sure if this causes any namespace conflicts.
import static org.mockito.Mockito.*;
import static org.mockito.MockitoAnnotations.initMocks;
import org.junit.*;
import static org.junit.Assert.assertThat;
import static org.hamcrest.CoreMatchers.*;
import atg.servlet.*;
import some.form.handler.FormHandler;
#RunWith(JUnit4.class)
public class FormHandlerTest {
#Mock DynamoHttpServletRequest request;
#Mock DynamoHttpServletResponse response;
FormHandler handler;
#Before
public void setup() {
initMocks(this);
handler = new FormHandler();
}
#Test
public void testSubmitHandlerRedirects() {
handler.handleSubmit(request, response);
verify(response).sendLocalRedirect(eq("/success.jsp"), eq(request));
assertThat(handler.getFormError(), is(false));
}
}
The basic idea is to set up custom behavior for mocks/stubs using when() on the mock object method invocation to return some test value or throw an exception, then verify() mock objects were invoked an exact number of times (in the default case, once), and do any assertions on data that's been changed in the form handler. Essentially, you'll want to use when() to emulate any sort of method calls that need to return other mock objects. When do you need to do this? The easiest way to tell is when you get NPEs or other runtime exceptions due to working with nulls, zeros, empty strings, etc.
In an integration test, ideally, you'd be able to use a sort of in-between mock/test servlet that pretends to work like a full application server that performs minimal request/session/global scope management. This is a good use for Arquillian as far as I know, but I haven't gotten around to trying that out yet.