EasyMock, JUnit, Java5 and generics...

  • Thread starter Darryl L. Pierce
  • Start date
D

Darryl L. Pierce

I'm trying to create a unit test. In the unit test I need to create a
mock java.util.List object. The List is going to be a collection of
WebForm objects, so the way it's declared using generics as
List<WebForm>. For my unit test I want to mock this List, but cannot
find a way to declare the mock object in my code. The declaration is as
follows:

private MockControl<List<WebForm>> controlWebforms;
private List<WebForm> webforms;

When I try to create the mock object, the following code is invalid:

controlWebforms = MockControl.createControl(List.class);
webforms = controlWebforms.getMock();

I get the error message "Type mismatch: cannot convert from
MockControl<List> to MockControl<List<WebForm>>" However, the following
declaration:

controlWebforms = MockControl.createControl(List<WebForm>.class);
webforms = controlWebforms.getMock();

is invalid. How do I reference the class for List<WebForm> to create my
mock object? Any ideas?
 
A

Andrew McDonagh

Darryl said:
I'm trying to create a unit test. In the unit test I need to create a
mock java.util.List object. The List is going to be a collection of
WebForm objects, so the way it's declared using generics as
List<WebForm>. For my unit test I want to mock this List, but cannot
find a way to declare the mock object in my code. The declaration is as
follows:

private MockControl<List<WebForm>> controlWebforms;
private List<WebForm> webforms;

When I try to create the mock object, the following code is invalid:

controlWebforms = MockControl.createControl(List.class);
webforms = controlWebforms.getMock();

I get the error message "Type mismatch: cannot convert from
MockControl<List> to MockControl<List<WebForm>>" However, the following
declaration:

controlWebforms = MockControl.createControl(List<WebForm>.class);
webforms = controlWebforms.getMock();

is invalid. How do I reference the class for List<WebForm> to create my
mock object? Any ideas?

Never mock the list, mock the list contents.

Also, unless you need to use a mock object framework, its usually easier
not to. Rolling your own fake and mock versions of your interfaces and
classes is very easy with Eclipse & IntelliJ, so Mocking frameworks
really come into themselves when you want to mock third party code.

YMMV

What is MockControl? Is this your own MVC/P class?

Andrew
 
D

Darryl L. Pierce

Andrew said:
Never mock the list, mock the list contents.

No, I don't care about the contents. I only care about mocking the List
itself. Why do you say the above about not mocking the list?

At any rate, I found the solution. It was to create the mock control as
follows:

private MockControl said:
Also, unless you need to use a mock object framework, its usually easier
not to. Rolling your own fake and mock versions of your interfaces and
classes is very easy with Eclipse & IntelliJ, so Mocking frameworks
really come into themselves when you want to mock third party code.

YMMV

What is MockControl? Is this your own MVC/P class?

It's a part of the EasyMock framework.
 
A

Andrew McDonagh

Darryl said:
No, I don't care about the contents. I only care about mocking the List
itself. Why do you say the above about not mocking the list?

I take it the list is the standard Java one?

And you are attempting to create a mock version of it?

Why?

We don't need to mock things that work or are easy to use.

I'd guess for your test, its the contents you don't care about but you
do care that a valid list object is used. So use a list, but fill it
with mock contents.

Can you post your test case code? ( I don't need to see the code its
testing)
At any rate, I found the solution. It was to create the mock control as
follows:




It's a part of the EasyMock framework.

right o..
 
D

Darryl L. Pierce

Andrew said:
I take it the list is the standard Java one?
Yes.

And you are attempting to create a mock version of it?

Yes. I'm mocking the response an instance of List would return to the
isEmpty() API.

Because I don't want to create an actual instance and have to populate
it with actual instances of my WebForm object. Hence the reason I'm
using EasyMock. :)
We don't need to mock things that work or are easy to use.

We need to mock those things which we do not want to have to actually
create. I don't want to create an instance of List<WebForm> because to
do so means I would then have to create actual instances of WebForm and
add them to the List when I want to have the isEmpty() method return false.
I'd guess for your test, its the contents you don't care about but you
do care that a valid list object is used. So use a list, but fill it
with mock contents.

Sorry, but you should really learn more about my requirements first
before saying whether I should or shouldn't mock a List or its
contents.. I don't care about the contents and, since I'm not writing
unit tests for java.util.List, dont' care about the concrete
implementations available for List. I only need to have a mock control
(in this case) either return true or false when the isEmpty() method is
invoked.
Can you post your test case code? ( I don't need to see the code its
testing)

Sure. Here ya go:

---8<[snip]---
package com.redhat.www.webforms.web;

import java.util.List;

import org.easymock.MockControl;
import org.springframework.validation.Errors;

import com.redhat.www.webforms.domain.WebForm;
import com.redhat.www.webforms.domain.WebFormListCommand;
import junit.framework.TestCase;

/**
* <code>TestWebFormListValidator</code> performs unit tests on the
* {@link WebFormListValidator} class.
*
* @author Darryl L. Pierce
*/
public class TestWebFormListValidator extends TestCase
{
private WebFormListValidator validator;
private WebFormListCommand command;

private MockControl<Errors> controlErrors;
private Errors errors;
private MockControl<? extends List> controlWebforms;
private List<WebForm> webforms;
private MockControl<? extends List> controlSelectedWebForms;
private List<WebForm> selectedWebForms;

protected void setUp() throws Exception
{
validator = new WebFormListValidator();
command = new WebFormListCommand();

controlErrors = MockControl.createControl(Errors.class);
errors = controlErrors.getMock();

controlWebforms = MockControl.createControl(List.class);
webforms = controlWebforms.getMock();
command.setWebforms(webforms);

controlSelectedWebForms =
MockControl.createControl(List.class);
selectedWebForms = controlSelectedWebForms.getMock();
command.setSelectedWebforms(selectedWebForms);
}

protected void tearDown() throws Exception
{
controlErrors.verify();
controlWebforms.verify();
controlSelectedWebForms.verify();
}

private void replayControls()
{
controlErrors.replay();
controlWebforms.replay();
controlSelectedWebForms.replay();
}

/**
* Tests the validator's response to a delete request without any forms
* present.
*/
public void testDeleteWithNoFormsPresent()
{
webforms.isEmpty();
controlWebforms.setReturnValue(true);
errors.reject("webform.error.delete-no-forms-present");
controlErrors.setVoidCallable();

replayControls();

validator.validate(command, errors);
}

/**
* Tests the validator's response to a delete request without any forms
* selected.
*/
public void testDeleteWithoutSelectedForms()
{
webforms.isEmpty();
controlWebforms.setReturnValue(false);
selectedWebForms.isEmpty();
controlSelectedWebForms.setReturnValue(true);
errors.reject("webform.required.delete-requires-selection");
controlErrors.setVoidCallable();

replayControls();

validator.validate(command, errors);
}

/**
* Test the validator's response to a delete request with at least
one form
* selected.
*/
public void testDeleteWithFormsSelected()
{
webforms.isEmpty();
controlWebforms.setReturnValue(false);
selectedWebForms.isEmpty();
controlSelectedWebForms.setReturnValue(false);

replayControls();

validator.validate(command, errors);
}
}
---8<[snip]---
 
A

Andrew McDonagh

Darryl said:
Yes. I'm mocking the response an instance of List would return to the
isEmpty() API.



Because I don't want to create an actual instance and have to populate
it with actual instances of my WebForm object. Hence the reason I'm
using EasyMock. :)



We need to mock those things which we do not want to have to actually
create. I don't want to create an instance of List<WebForm> because to
do so means I would then have to create actual instances of WebForm and
add them to the List when I want to have the isEmpty() method return false.



Sorry, but you should really learn more about my requirements first
before saying whether I should or shouldn't mock a List or its
contents.. I don't care about the contents and, since I'm not writing
unit tests for java.util.List, dont' care about the concrete
implementations available for List. I only need to have a mock control
(in this case) either return true or false when the isEmpty() method is
invoked.

so why not just instantiate the list and leave it empty?

Is the list the webforms one in setup that we are talking about, or some
other one?
 
D

Darryl L. Pierce

Andrew said:
so why not just instantiate the list and leave it empty?

Why do that when I can mock the object and control exactly how the
mocked object responds to method invokations? The point of mocking
objects is to create a phantom object that does exactly what you want so
that you can explicitly test another object. I don't need an actual
List: I just need something that has the same type as List and which
returns false in one case and true in other cases when the isEmpty()
method is invoked on it.

If I create an actual List I then have to create actual objects (whether
they're dumby objects or not is irrelevant) to populate it in order to
have isEmpty() return false. I don't want to do that, it's outside of
the scope of my unit test. My unit test doesn't _care_ about that level
of detail, so doing it is extra work that doesn't give any added benefit.
Is the list the webforms one in setup that we are talking about, or some
other one?

That's the one.
 
A

Andrew McDonagh

Darryl said:
Why do that when I can mock the object and control exactly how the
mocked object responds to method invokations? The point of mocking
objects is to create a phantom object that does exactly what you want so
that you can explicitly test another object. I don't need an actual
List: I just need something that has the same type as List and which
returns false in one case and true in other cases when the isEmpty()
method is invoked on it.

If I create an actual List I then have to create actual objects (whether
they're dumby objects or not is irrelevant) to populate it in order to
have isEmpty() return false. I don't want to do that, it's outside of
the scope of my unit test. My unit test doesn't _care_ about that level
of detail, so doing it is extra work that doesn't give any added benefit.



That's the one.

Do you mock String, Integer, Boolean, Map, etc, too?


List webforms = new List();

assertTrue(webforms.isEmpty());

whats the difference you see between that List and your mock list - you
still have to create an instance of your mock list.

I understand the benefit of mocks, we can prime them for expected
behavior calls and values. Though I'm at a loss to understand why you
need to mock a list....


Mocking any Java class is rare (though not non-existent) in the junit
community.

Just curious you see...


I'm going to know up your tests and the command & validator classes
without mocks, just to see what the difference is like...but give me a
few hours as I'm about to go cook the dinner :)

Andrew
 
D

Darryl L. Pierce

Andrew said:
Do you mock String, Integer, Boolean, Map, etc, too?

No, no, no, yes, depends. You can only mock an interface, not a class.
List webforms = new List();

assertTrue(webforms.isEmpty());

I'm not testing that. That's why I said previously that you ought to
understand my requirements before suggesting that what I'm doing is
incorrect. I'm testing a class that checks a List for whether it
contains any elements and branches based on that. I have no need for
creating/populating an instance of a class that implements List when I
can achieve *the same results* using a mock object alone. And I can do
it in *fewer lines of code* with mock objects.
whats the difference you see between that List and your mock list - you
still have to create an instance of your mock list.

And I can script the responses that the mock object returns without
having to create any *more* objects. One mock and I'm done. With what
you're suggesting, I have to create *more* objects if I want isEmpty()
to return false. With mock objects, I don't.
I understand the benefit of mocks, we can prime them for expected
behavior calls and values. Though I'm at a loss to understand why you
need to mock a list....

That's because you don't seem to understand my requirements, which is
understandable.
Mocking any Java class is rare (though not non-existent) in the junit
community.

Rare based on what metric or study?
Just curious you see...

I'm going to know up your tests and the command & validator classes
without mocks, just to see what the difference is like...but give me a
few hours as I'm about to go cook the dinner :)

Nah, save yourself the effort. What you're suggesting provides no
benefit and involves creating more objects than is necessary. The one
example I provided is hardly a good example anyway. HttpServletRequest
is a better example and is also a Java class.
 
A

Andrew McDonagh

Darryl said:
No, no, no, yes, depends. You can only mock an interface, not a class.

With certain mocking frameworks anyway...

Its sometimes beneficial to derive from a class and over ride its
methods to serve like a Fake or Mock.
I'm not testing that. That's why I said previously that you ought to
understand my requirements before suggesting that what I'm doing is
incorrect.

Yes, sorry, thats not what I was trying to show. I was merely trying to
show that a (actually ArrayList - missed that above) list can be created
without any elements, and its current implementation would satisfy the
isEmpty(). But then I could be missing some point about your usage of
the list - you haven't made it clear...
I'm testing a class that checks a List for whether it
contains any elements and branches based on that. I have no need for
creating/populating an instance of a class that implements List when I
can achieve *the same results* using a mock object alone. And I can do
it in *fewer lines of code* with mock objects.



And I can script the responses that the mock object returns without
having to create any *more* objects. One mock and I'm done. With what
you're suggesting, I have to create *more* objects if I want isEmpty()
to return false. With mock objects, I don't.

What more objects? its one ArrayList object containing nothing.

Thats the same as one MockList containing nothing.
That's because you don't seem to understand my requirements, which is
understandable.



Rare based on what metric or study?

Based upon neither...just personal long standing usage of JUnit,
coaching of TDD using JUnit and discussing it on various JUnit forums.

If you haven't come across it, I can recommend the JUnit Yahoo group.
You'll find many good people there willing to help, including the JUnit
authors (Beck et al) and various book authors...
Nah, save yourself the effort. What you're suggesting provides no
benefit and involves creating more objects than is necessary.

right - o
The one example I provided is hardly a good example anyway. HttpServletRequest
is a better example and is also a Java class.

What about HttpServletRequest?

Mocking it?
 
D

Darryl L. Pierce

Andrew said:
With certain mocking frameworks anyway...


But not with what I'm using.
Its sometimes beneficial to derive from a class and over ride its
methods to serve like a Fake or Mock.


Except when that other class is also one that's tested in your project,
in which case you can't be assured that a failure is a result of the
class you're testing or the one you're using to do the testing. Also
your method does not give me any guarantee that any particular method
(such as isEmpty() in this case) was even called in the first place.
EasyMock reports the test as a failure if the methods you expected to be
called weren't called. Stub objects do not do that for you unless you
first write *even more code* to track that. And now you've created even
*more* work just to accomplish a unit test.
understand my requirements before suggesting that what I'm doing is
incorrect.
Yes, sorry, thats not what I was trying to show. I was merely trying
to show that a (actually ArrayList - missed that above) list can be
created without any elements, and its current implementation would
satisfy the isEmpty(). But then I could be missing some point about
your usage of the list - you haven't made it clear...


I've made it abundantly clear: the class I'm testing will call the
isEmpty() method on the List it has and branch based on whether it gets
a true or a false back.
having to create any *more* objects. One mock and I'm done. With what
you're suggesting, I have to create *more* objects if I want isEmpty()
to return false. With mock objects, I don't.
What more objects? its one ArrayList object containing nothing.


I don't just need a List that contains nothing. I also need to test when
isEmpty() returns *false*. In order to do it your way I need to create
*more* objects and put them into the List, objects which themselves will
never be used in my unit tests. Creating objects that will never be used
is a waste.
Thats the same as one MockList containing nothing.


Mate, I've said a few times that that's not the *only* List I'm using.
Based upon neither...just personal long standing usage of JUnit,
coaching of TDD using JUnit and discussing it on various JUnit forums.


So you mean *you* don't do it? Okay, then say so. My experience is
different from yours: all I've ever seen have mocked anything that was
not directly being tested.
If you haven't come across it, I can recommend the JUnit Yahoo group.


Already a member.
You'll find many good people there willing to help, including the
JUnit authors (Beck et al) and various book authors...


I have _JUnit In Action_. I'm not ignorant of JUnit, thank you.
HttpServletRequest is a better example and is also a Java class.
What about HttpServletRequest?

Mocking it?


Yes. It's a Java class. Simply creating a stubbed version of it is a
whole extra programming effort. If you're testing a servlet is it really
more practical to create a scaffold version of HttpServletRequest? What
do you do about the contained HttpSession? If your unit test touches it
then you now have to stub that as well. With Easy Mock you can mock the
whole lot of those objects and pass back and control the whole situation.

Again, the whole point I'm making now is that dismissing mocking
Collection interfaces or anything else that's "a Java class" or saying
that "we don't need to mock things that work" is short sighted. Mocking
objects is a means to an end and not the end itself, and creating a real
instance of the type rather than using a mocked version just because
it's part of the Java API is not necessarily the right/better way, nor
is mocking them automatically the wrong/worse way. As long as the
desired outcome's achieved, then it's that's what matters. And if it's
done in the fewest lines of code or in the simplest, least complicated
way then you have a better assurance that your test isn't being
influenced by possibly buggy testing code, something which is more
likely to happen if you're writing extra classes *just* for the tests to
consume.
 
A

Andrew McDonagh

Darryl said:
I have _JUnit In Action_. I'm not ignorant of JUnit, thank you.

wasn't trying to imply you were..., I'm curious as to why you wanted to
do it that way... thats all. Call it a learning exercise for me if
you'd like.
 
D

Darryl L. Pierce

Andrew said:
wasn't trying to imply you were..., I'm curious as to why you wanted to
do it that way... thats all. Call it a learning exercise for me if
you'd like.

K, no prob. :)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top