File APIs for Java Developers
Manipulate DOC, XLS, PPT, PDF and many others from your application.
http://aspose.com/file-tools
The moose likes Testing and the fly likes Help....Junit mocking problem Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login
JavaRanch » Java Forums » Engineering » Testing
Bookmark "Help....Junit mocking problem" Watch "Help....Junit mocking problem" New topic
Author

Help....Junit mocking problem

Nivan scorp
Greenhorn

Joined: Dec 06, 2004
Posts: 23
Hi,
I have written written a JUnit to test an action file.The problem is that the method "submit()" in my Action file calls a utility class function
before calling the service which I have mocked in my JUnit.

Hence, when I run my JUnit , I get an error saying "junit.framework.AssertionFailedError: Unexpected method call "


Can anyone suggest how I can overcome this problem???
If I comment out the utility class function call, then the test runs fine...


Thanks,
Nivan.
Jeanne Boyarsky
internet detective
Marshal

Joined: May 26, 2003
Posts: 30123
    
150

Nivan,
Welcome to JavaRanch! What mock objects framework are you using? Easymock? Jmock?

To solve the problem, you need to tell the mock about all the calls it will be receiving in the setup method.


[Blog] [JavaRanch FAQ] [How To Ask Questions The Smart Way] [Book Promos]
Blogging on Certs: SCEA Part 1, Part 2 & 3, Core Spring 3, OCAJP, OCPJP beta, TOGAF part 1 and part 2
Jeanne Boyarsky
internet detective
Marshal

Joined: May 26, 2003
Posts: 30123
    
150

Also some mock object frameworks, like easyMock, let you set up a "nice control." This is less picky about what calls it receives.
Nivan scorp
Greenhorn

Joined: Dec 06, 2004
Posts: 23
I am using Easy Mock.
This is how my Test file loooks like followed by my Action file.



* * * The last line i.e LatentRequestUtils.setupEmails(context, request, this); is the problem. If I comment this line, it works fine...else the unexpected methoid call error is displayed.
[ December 06, 2004: Message edited by: Lasse Koskela ]
Jeanne Boyarsky
internet detective
Marshal

Joined: May 26, 2003
Posts: 30123
    
150

Nivan,
The problem here is that the method is static. Static methods are impossible to mock out directly. So you will need to refactor this code to make it more testable. If you don't control the static class, you can create a wrapper for it.

What does the static method do? If it is calling your mock and you don't want to mock that method, you need to figure out what methods the static method calls.
Nivan scorp
Greenhorn

Joined: Dec 06, 2004
Posts: 23
Hi Jeanne Boyarsky,
Nivan scorp
Greenhorn

Joined: Dec 06, 2004
Posts: 23
Hi Jeanne Boyarsky,
Well, I refactored the LatentRequestUtil code so that the methods are no longer static. Next, I created a mock class for the LatentRequestUtil called MockLatentRequestUtil and even made a mock interface LatenRequestUtil . Using this mock interface, I created a control in my test.


My test code now looks like this..

************START**************

public void setUp() throws Exception {
super.setUp();
controlService =MockControl.createControl(LatentRequestService.class);
mockService = (LatentRequestService) controlService.getMock();
setServletConfigFile("/WEB-INF/web.xml");
setConfigFile("/WEB-INF/struts-config-latentrequest.xml");
setRequestPathInfo("/EmailUs.do");

latentRequestControl =MockControl.createControl(MockLatenRequestUtils.class);
mockLatentRequestUtils = (MockLatenRequestUtils) latentRequestControl.getMock();

baseDispatchControl =MockControl.createControl(MockBaseLookupDispatchActionInterface.class);
mockBaseDispatchService = (MockBaseLookupDispatchActionInterface) baseDispatchControl.getMock();

customerProfileControl =MockControl.createControl(CustomerProfileService.class);
mockCustomerProfileService = (CustomerProfileService) customerProfileControl.getMock();
}


public void testSetup() throws Exception {

ActionContextImpl acc = new ActionContextImpl(getSession());
CustomerProfileService cipl = new CustomerProfileServiceImpl();
baseDispatchControl.expectAndReturn(mockBaseDispatchService.createService(CustomerProfileService.class,acc),cipl);


GetEmailAddressesServiceResponse getEmailAddressesServiceResponse = new GetEmailAddressesServiceResponse();
customerProfileControl.expectAndReturn(mockCustomerProfileService.getCurrentEmailAddresses(),getEmailAddressesServiceResponse);


EmailAddressImpl emailAddressImpl = new EmailAddressImpl();
emailAddressImpl.setEmailAddressString("Gooood");

EmailUsAction ac = new EmailUsAction();
latentRequestControl.expectAndReturn(mockLatentRequestUtils.setupEmails(mockServicea,mockServicehtt,(BaseLookupDispatchAction)ac),emailAddressImpl);

serviceResponse = new EmailUsDisplayServiceResponse();
List list = new ArrayList();
list.add("Test Account");
serviceResponse.setAccounts(list);

controlService.expectAndReturn(mockService.getEmailUsDisplay(),serviceResponse);

// set up mock ServiceFactory to return mock LatentRequestService
setupMockServiceFactory();

// set up mock page flow manager to expect call to createTransaction()
expectCreatePageFlowTransaction();

// Add request parameter so that setup() method called
addRequestParameter(PARAM_DISPATCH_METHOD, METHOD_SETUP);

replayMocks();
System.out.println("REPLAY OVER ");

// call actionPerform to kick off Struts Test Case
//doTheOuterCallTestFirst();
actionPerform();

// make sure correct ActionForward and Path returned
verifyTilesForward("edit", "emailUsRequestEdit");
System.out.println(" verifyTilesForward VERIFIED ");
// make sure there are no ActionErrors
verifyNoActionErrors();

// make sure ViewBean has correct values
EmailUsForm form = (EmailUsForm) getActionForm();

// make sure all expected methods calls to mock objects were called
verifyMocks();
}

**********END**********

My LatentRequestUtil code looks as


-----------LatentRequestUtil ---START----------------

public EmailAddressReadOnly setupEmails(ActionContext context,
HttpServletRequest request,
BaseLookupDispatchAction action)
throws LocalSystemException, RemoteSystemException {

// create a CustomerProfileService
CustomerProfileService service;
service = (CustomerProfileService) action.createService(
CustomerProfileService.class, context);

// use the service to get the customer's current email addresses
GetEmailAddressesServiceResponse serviceResponse;
serviceResponse = service.getCurrentEmailAddresses();

// fill in the form with the email addresses retrieved
EmailAddressesReadOnly emailAddresses = serviceResponse.getEmailAddresses();
request.setAttribute(EMAIL_ADDRESSES, emailAddresses);

EmailAddressReadOnly emailAddress =
emailAddresses.getEmailAddressOfType(EmailAddressType.PRIMARY);
request.setAttribute(PRIMARY_EMAIL_ADDRESS, emailAddress);

emailAddress =
emailAddresses.getEmailAddressOfType(EmailAddressType.SECONDARY);
request.setAttribute(SECONDARY_EMAIL_ADDRESS, emailAddress);

emailAddress =
emailAddresses.getEmailAddressOfType(EmailAddressType.OTHER);
request.setAttribute(OTHER_EMAIL_ADDRESS, emailAddress);

emailAddress =
emailAddresses.getEmailAddressOfType(EmailAddressType.WIRELESS);
request.setAttribute(WIRELESS_EMAIL_ADDRESS, emailAddress);
System.out.println("In EmailAddressReadOnly end of method setupEmails() ");
System.out.println("In EmailAddressReadOnly end of method setupEmails() Returning--> "+emailAddresses.getEmailAddressOfType(EmailAddressType.PRIMARY));
return emailAddresses.getEmailAddressOfType(EmailAddressType.PRIMARY);
}


--------------END--------------


So, you see that I have mocked all the methtod calls, so then why does my control go to the setupEmails() method in LatentUtils when it encounters the code "latentutil.setupEmails() in the Action setup method which can be seen under...

ACTION method:-

public ActionForward setup(
ActionContext context,
ActionMapping mapping,
BaseActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws LocalSystemException {

context.getPageFlowManager().createTransaction(form, mapping, request);

LatentRequestUtils lu = new LatentRequestUtils();
lu.setupEmails(context, request, this);
EmailUsForm actionForm = (EmailUsForm) form;
EmailUsViewBean emailUsViewBean = new EmailUsViewBean();

LatentRequestService service = (LatentRequestService) createService(
LatentRequestService.class, context);
serviceResponse = service.getEmailUsDisplay();
return mapping.findForward(FORWARD_ID_EDIT);

}
Jeanne Boyarsky
internet detective
Marshal

Joined: May 26, 2003
Posts: 30123
    
150

Nivan,
That code is much more testable. One more step and you are there. (I assume LatentRequestUtils has an interface since mock objects use interfaces.)

Here, LatentRequestUtils is being instantiated directly. So the method doesn't know you want to substitute a mock. If this object is stored as an instance variable, you can substitute it in at test time and have the regular code function normally. For example,

The test then assigns the mock using the setter.
Jeanne Boyarsky
internet detective
Marshal

Joined: May 26, 2003
Posts: 30123
    
150

Also, two comments on the forum:
1) If you submit a post before you meant to, you can edit it by clicking the icon that looks like a paper and pencil. It's the third icon on the line with your name. Alternatively, you can delete a post this way.
2) Please use code tags in the future like you did in the first post. It makes long bodies of code much easier to read.
Nivan scorp
Greenhorn

Joined: Dec 06, 2004
Posts: 23
Hi Mr Jeanne Boyarsky ,
Thats the whole problem. The LatentRequestUtils is just a simple java class with no interfaces!! That was the reason I have constructed a MockLatentRequestUtils and a MockLatentRequestUtilsIf, but then how do I make the test class enter the method in my MockLatentRequestUtils and not the actual LatentRequestUtils class?
Jeanne Boyarsky
internet detective
Marshal

Joined: May 26, 2003
Posts: 30123
    
150

Nivan,
You need to add an interface to the LatentRequestUtils class. Since you already have access to this class (from changing the methods to instance from static), this shouldn't be a big deal.

(Just so you know, I'm female.)
Nivan scorp
Greenhorn

Joined: Dec 06, 2004
Posts: 23
Hi Jeanne,
Now, we're on the same plane. The whole point is whether I can do it without creating an interface for the LatentRequestUtils class? Since it is an existing class, its a risky thing because it could have been used at many other instances. So, my intention is to somehow test it without changing the form of the LatentRequestUtil class by introducing an interface for it. Can I do it?

Thanks,
Navin. ((Sorry abt that 'Mr' title..ooops!!),
Lasse Koskela
author
Sheriff

Joined: Jan 23, 2002
Posts: 11962
    
    5
Originally posted by Nivan scorp:
The whole point is whether I can do it without creating an interface for the LatentRequestUtils class? Since it is an existing class, its a risky thing because it could have been used at many other instances. So, my intention is to somehow test it without changing the form of the LatentRequestUtil class by introducing an interface for it. Can I do it?

If your starting point is this:

you might go this way:

Basically you'd leave the class with static methods as a dumb dispatcher which uses either the default implementation or the implementation you've configured the class with using the setter.

(Then again, I've worked over 11 hours today and woke up at 4:30 am so I might've completely misunderstood what you were asking for...)


Author of Test Driven (2007) and Effective Unit Testing (2013) [Blog] [HowToAskQuestionsOnJavaRanch]
Jeanne Boyarsky
internet detective
Marshal

Joined: May 26, 2003
Posts: 30123
    
150

Lasse,
Why are we making this so complicated? The LatentRequestUtil class already has instance method. All Nivan needs is to do an "extract interface" on it. In other words, add an interface and put the methods in it. This doesn't involve changing any clients (except the one under test.)
Lasse Koskela
author
Sheriff

Joined: Jan 23, 2002
Posts: 11962
    
    5
Agreed.
Nivan scorp
Greenhorn

Joined: Dec 06, 2004
Posts: 23
I know that this thread has been going on for quite some time and I really appreciate you guys' inputs.

A couple of points:

1. I am not supposed to introduce an interface for LatentRequestUtil;
2. If you see the code for LatentRequestUtil, you will see that there is
a service being called. I have reached a point where I have even mocked this service(earlier I used to get a unexpected service call error). Now, my only problem is that I am unable to make the following service call "serviceResponse = service.getCurrentEmailAddresses()" in my LatentRequestUtils file return a hardcoded serviceResponse object.

In my test file, I have done the following,
GetEmailAddressesServiceResponse emailAddressesServiceResponse = new GetEmailAddressesServiceResponse();
emailAddressesServiceResponse.setValues("My Own Hardcoded values");
basecontrolService.expectAndReturn(basemockService.getCurrentEmailAddresses(),emailAddressesServiceResponse);

But the getCurrentEmailAddresses() method upon being encountered in LatentRequestUtils does not return the emailAddressesServiceResponse object I created with my hard coded values when I call actionPerform() from my test file.
How do I force this?


Thanx,
Nivan
Nivan scorp
Greenhorn

Joined: Dec 06, 2004
Posts: 23
HI,
Also, do you have any clue what this error means:-
"missing behavior definition for last method call on the mock"

Thanx,
Nivan.
Lasse Koskela
author
Sheriff

Joined: Jan 23, 2002
Posts: 11962
    
    5
Originally posted by Nivan scorp:
do you have any clue what this error means:-
"missing behavior definition for last method call on the mock"

That error means that the mock object received a method call it wasn't prepared for. This could be because you forgot to call replay() on your control, or because you forgot to call that method before calling replay().
Jeanne Boyarsky
internet detective
Marshal

Joined: May 26, 2003
Posts: 30123
    
150

I am not supposed to introduce an interface for LatentRequestUtil;

Why not?
Nivan scorp
Greenhorn

Joined: Dec 06, 2004
Posts: 23
Hi,
By saying "I am not supposed to introduce an Interface", its because this Utility class "LatentRequestUtils" has been in use in a lot of other classes and thus introducing an interface would mean that I have to first find out all the usages and chage acordingly and then test it!! It will take quite a long time to do so!!!

Thanks,
Nivan.
Jeanne Boyarsky
internet detective
Marshal

Joined: May 26, 2003
Posts: 30123
    
150

Nivan,
You don't have to change the other classes calling the LatentRequestUtils class. In fact, they can call the original static method. Just change the static method to point to the new instance method. Only the class you are trying to test needs to know about the interface. (This is what I was getting at in my question to Lasse.) For example:


Existing code calls:
LatentRequestUtils.setupEmails()

New code calls:
ILatentRequestUtils utils = new LatentRequestUtils();
utils.setupEmailsForInstance();

I completely agree that you shouldn't change all the legacy code.
Nivan scorp
Greenhorn

Joined: Dec 06, 2004
Posts: 23
Hi Jeanne,
I get what you are saying with respect to the new Interface you suggested. That seems to be a nice way to get around without affecting the places where it has already been used.

But, keeping aside the Interface option aside, I have made a pretty decent progress, wherein I mocked the service that has been called in the LatentRequestUtils file (CustomerProfileService specefically).
Now, I can see that the control reaches the setupEmails() method in this urility class and the createService() method call is actually returning me a mock service(I verified this by printing the getClass() of the service returned and it prints getClass() --> $Proxy.)
But the next statement in the util class after the log statement throws the error "Unable to get user's current email addresses:
java.lang.IllegalStateException: missing behavior definition for last method call on the mock" :-
service = (CustomerProfileService) action.createService(CustomerProfileService.class, context);
log.debug("service.getClass()-->"+service.getClass());
serviceResponse = service.getCurrentEmailAddresses();

So, though the service seems to have been mocked successfully, the return value is not being set properly thought I have specified it in my test file using the following code. Can you spot if I have made some serious mistakes.
I will include my test file and the setup() method of the Action file below. Tis might give u a clearer picture.

----------------------------------------------------------------------------
//****Test File ****START//

public void setUp() throws Exception {
super.setUp();
controlService =MockControl.createControl(LatentRequestService.class);
mockService = (LatentRequestService) controlService.getMock();(LatentRequestService.class);
controlService =MockControl.createControl(LatentRequestService.class);
basecontrolService = MockControl.createControl(CustomerProfileService.class);
basemockService = (CustomerProfileService) basecontrolService.getMock();
}

public void testSetup(){
//This is for the utility class mocking
GetEmailAddressesServiceResponse emailAddressesServiceResponse = new GetEmailAddressesServiceResponse();
basecontrolService.expectAndReturn(basemockService.getCurrentEmailAddresses(),emailAddressesServiceResponse);

//This is for mocking the other service*/

serviceResponse = new EmailUsDisplayServiceResponse();
List list = new ArrayList();
list.add("Testing");
serviceResponse.setAccounts(list);
mockService.getEmailUsDisplay();
controlService.setReturnValue(serviceResponse);
setupMockServiceFactory();
expectCreatePageFlowTransaction();
addRequestParameter(PARAM_DISPATCH_METHOD, METHOD_SETUP);
replayMocks();
actionPerform();
verifyNoActionErrors();
verifyMocks();
}

protected void verifyMocks() throws Exception {
super.verifyMocks();
controlService.verify();
}

protected void replayMocks() {
super.replayMocks();
controlService.replay();
}
private void setupMockServiceFactory()
throws ServiceFrameworkException {
// set up mock ServiceFactory to return mock LatentRequestService
mockServiceFactory.createService(LatentRequestService.class);
controlServiceFactory.setReturnValue(mockService);
}

//****Test File ****END//

----------------------------------------------------------------------------

----------------------------------------------------------------------------
//****Action File ****START//
public ActionForward setup(
ActionContext context,
ActionMapping mapping,
BaseActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws LocalSystemException {
try {
defaultEmailAddress = LatentRequestUtils.setupEmails(context, request, this);
} catch (Exception e) {
ActionForward actionForward =
mapping.findForward(FORWARD_ID_NO_RETRY_ERROR);
return actionForward;
}
LatentRequestService service = (LatentRequestService) createService(
LatentRequestService.class, context);
serviceResponse = service.getEmailUsDisplay();
return mapping.findForward(FORWARD_ID_EDIT);
}
//****Action File ****END//
---------------------------------------------------------------------------


I have a feeling its mainly to do with the problem of the invocation of the replay() and verifyMocks() method calls now.

Your help is immensely appreciated!!.

Thanks,
Nivan.
[ December 12, 2004: Message edited by: Nivan scorp ]
Nivan scorp
Greenhorn

Joined: Dec 06, 2004
Posts: 23
Hey guys,
I am just hoping somebody throws some light based on my last description of the situation and the code provided. Am having sleepless nights!!


Thanks,
Nivan.
Lasse Koskela
author
Sheriff

Joined: Jan 23, 2002
Posts: 11962
    
    5
Are you calling replay() on all MockControl objects you're using?
Nivan scorp
Greenhorn

Joined: Dec 06, 2004
Posts: 23
Sure it was the replay() not being called properly.
Thanks Lasse/Jeanne.


Well, my JUnit finally runs through with
the services being mocked perfectly!! :-))

Now, I am running into one peculiar error and I guess this
should be soemthing some of you could spot right away.
I have three test methods namely
1. testSetup,
2. testSubmit and
3. testApply.

When I run my Test class, the control correctly flows
from one method to the next for all methods that start with
the word 'test' but they all seem to enter the method named
"unspecified" in my Action file which always point to the setup() method.

This is how the "unspecified" method looks like:-
--------------------------CODE-------------------------------------
public ActionForward unspecified(ActionContext context,
ActionMapping mapping,
BaseActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws LocalSystemException {

return setup(context, mapping, form, request, response);
}
--------------------------CODE-------------------------------------

What is the use of this "unspecified" method and when does the
control go there?

I have correctly used the
"addRequestParameter(PARAM_DISPATCH_METHOD, METHOD_SETUP);"
function in all my test methods too!!

The problem I just defined
occurs only because my Action extends BaseLookupDispatchAction.

If I make my Action extends BaseDispatchAction, then my test runs through
fine and it enters all the methods in order!!!

Is there some variation in the test class for these two cases?


Any clues??


Thanks ,
Nivan.
Nivan scorp
Greenhorn

Joined: Dec 06, 2004
Posts: 23
Doesnt anyone have a clue on the difference betweeen handling the problem I stated between BaseDispatchAction and BaseLookupDispatchAction.

Am justhoping someone knows it!!


Thanks,
Nivan.
Lasse Koskela
author
Sheriff

Joined: Jan 23, 2002
Posts: 11962
    
    5
Maybe you should try asking about the difference between the two in our Web Application Frameworks forum or just dive inside the Struts source code to figure out what might be the cause for what you're observing.
 
I agree. Here's the link: http://aspose.com/file-tools
 
subject: Help....Junit mocking problem
 
Similar Threads
Problem to initialize the request parameter of the HttpServlet from any JUnit test class
A threading doubt
SetUp getting called for every test method
EJB Testing using JUnit
JUnit problem with addRequestParameter . HELP * * *