Question

I want to use junit tests in my next project, but I am unsure which of the several mock packages I should use. I also read through a couple of tutorials but I did not find info how to solve the specific problem outlined below. Maybe the feature is not available in the packages that I have checked out.

Here's the problem: I want to write an email filter class which iterates over a List<javax.mail.Message> and filters the email messages by subject, date, from, to etc.The code to test looks like this:

public List<Message> doFilter(List<Message> messageList) {

    List<Message> newList = new ArrayList<Message>(messageList.size());

    try {
        for (Message message: messageList) {
            if (start != null) {
                Date sentDate = message.getSentDate();
                if (sentDate == null || sentDate.before(start))
                    continue;
            }
            if (end != null) {
                Date receivedDate = message.getReceivedDate();
                if (receivedDate == null || receivedDate.after(end))
                    continue;
            }
            newList.add(message);
        }
    }
    catch(Exception e) {
        e.printStackTrace();
    }
    return newList;
}

So the obvious test case is to construct a List with a few messages and check whether the new list returned by the filter contains the right messages.

But javax.mail.Message is abstract and can't be instantiated directly. I would need to set up a real email store to do that, including account name and password.

So my questions are:

  • How do I mock a few javax.mail.Message objects with different values so that my filter class can call message.getSentDate() and other Message methods, retrieving the values that I have defined in the test setup code?

  • Which of the mock packages are best suited for this kind of problem?

All answers are really appreciated.

Was it helpful?

Solution

Just create a class, MockMessage that extends Message. Then you can implement or override the behaviour you want to mock.

As for mocking frameworks, I've found these more trouble than they are worth, but YMMV.

OTHER TIPS

I have tried both approaches to learn about the advantages and disadvantages:

Approach 1: create a new MockMessage class that implements only those methods I need in my filter code, then write standard JUnit tests.

The problem with the missing abstract method is actually trivial, as those methods can be generated by the IDE I am using (they will be empty but they are not needed by the code under test anyway). No Mock package is actually used.

public class MockMessage extends Message {

    private Date sentDate;

    public MockMessage(Date sentDate) {
        this.sentDate = sentDate;
    }

    public Date getSentDate() throws MessagingException {
        return sentDate;
    }

    // the rest of the required methods can easily be generated by the IDE
    ...
}


public void testFilter1() {

    try {
        DateTimeFormatter dtf = DateTimeFormat.forPattern("yyyy-MM-dd hh:mm:ss");
        final Date date0 = dtf.parseDateTime("2011-05-19 05:51:26").toDate();
        final Date date1 = dtf.parseDateTime("2011-05-19 05:51:27").toDate();
        final Date date2 = dtf.parseDateTime("2011-05-19 05:51:28").toDate();

        final List<Message> mockMessages = Arrays.asList(
            (Message)new MockMessage(date0),
            (Message)new MockMessage(date1),
            (Message)new MockMessage(date2)
        );

        MessageFilter filter = new ByDate(date1, null);
        List<Message> result = filter.doFilter(mockMessages);
        assertEquals(result.size(),2);
        assertEquals(result.get(0).getSentDate(),date1);
        assertEquals(result.get(1).getSentDate(),date2);
    }
    catch(Exception e) {
        fail(e.getMessage());
    }
}

Approach 2: using jMock and the ClassImposteriser. The amount of code is a bit less than in approach 1 and no extra MockMessage class is required.

I still prefer the first approach as it is simpler to understand. The second approach may be useful in other situations where objects are much more difficult to instantiate than javax.mail.Message .

public void testFilter2() {

    try {
        DateTimeFormatter dtf = DateTimeFormat.forPattern("yyyy-MM-dd hh:mm:ss");
        final Date date0 = dtf.parseDateTime("2011-05-19 05:51:26").toDate();
        final Date date1 = dtf.parseDateTime("2011-05-19 05:51:27").toDate();
        final Date date2 = dtf.parseDateTime("2011-05-19 05:51:28").toDate();

        Mockery mockery = new Mockery();
        mockery.setImposteriser(ClassImposteriser.INSTANCE);

        final List<Message> mockMessages = Arrays.asList(
            mockery.mock(Message.class, "message 1"),
            mockery.mock(Message.class, "message 2"),
            mockery.mock(Message.class, "message 3")
        );

        mockery.checking(new Expectations() {{
           allowing(mockMessages.get(0)).getSentDate(); will(returnValue(date0));
           allowing(mockMessages.get(1)).getSentDate(); will(returnValue(date1));
           allowing(mockMessages.get(2)).getSentDate(); will(returnValue(date2));
        }});

        MessageFilter filter = new ByDate(date1, null);
        List<Message> result = filter.doFilter(mockMessages);
        assertEquals(result.size(),2);
        assertEquals(result.get(0).getSentDate(),date1);
        assertEquals(result.get(1).getSentDate(),date2);
    }
    catch(Exception e) {
        fail(e.getMessage());
    }
}

Most mocking frameworks should have no trouble with this, although the fact that you're mocking class rather than an interface makes it slightly more awkward.

Here's a simple example using JMock (the framework I'm most familar with):

Mockery mockery = new Mockery();
mockery.setImposteriser(ClassImposteriser.INSTANCE);

final List<Message> mockMessages = asList(
    mockery.mock(Message.class, "message1"),
    mockery.mock(Message.class, "message2")
);

mockery.checking(new Expectations() {{
   allowing(mockMessages.get(0)).getSubject(); will(returnValue("some subject"));
   allowing(mockMessages.get(1)).getSubject(); will(returnValue("some other subject"));
}});

Normally, I would discourage people from mocking this sort of thing, and just create the objects directly, but the JavaMail API is so awkward to use, it's understandable to not to want to do that.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top