Search code examples
javajsfjunitobserver-patterneasymock

Junit and EasyMock unexpected failure


Hello fellow programmers. I have looked through dozens of "suggested" remedies on the internet (inclusively on stackoverflow) before I decided to post it here. But to no avail.

So. I started a small project in JSF. After failing to test the relationship between UIComponents and Managed Beans using JSFUnit and Apache Cactus (which is another sad story), I hoped to be able to at least test the domain model units. Alas.

I have this POJO:

public class AppointmentFormBean implements Serializable, Observed{
private Date selectedDate;
private ArrayList<VGObserver> observers;
/**
 * Creates a new instance of AppointmentFormBean
 */
public AppointmentFormBean() {
    observers = new ArrayList<>();
}

public Date getSelectedDate() {
    return selectedDate;
}

public void setSelectedDate(Date selectedDate) {
    this.selectedDate = selectedDate;
    notifyVGObservers();
}

@Override
public void registerVGObserver(VGObserver o) {
    this.observers.add(o);
}

@Override
public void removeVGObserver(VGObserver o) {
    int i = observers.indexOf(o);
    if(i>=0)
        observers.remove(o);
}

@Override
public void notifyVGObservers() {
    observers.stream().map((observer1) -> (VGObserver) observer1).forEach((observer) -> {
        observer.update(selectedDate);
    });
}

}

(I intentionally omitted JSF annotations - they are irrelevant for the test I ran)

The Observed interface that this POJO implements looks like this:

public interface Observed {
public void registerVGObserver(VGObserver o);
public void removeVGObserver(VGObserver o);
public void notifyVGObservers();

}

Yip, copied from Head First Design Patterns, who had read it :-)

VGObserver, mentioned above - is another interface, which will "listen" to the changes in the classes implementing Observed:

public interface VGObserver {
public void update(Date selectedDate);

}

I have big plans for this design, as eventually VGObserver (concrete class implementing it) will try to retrieve a REST resource - weather for the passed date. But we digress.

Finally I've built my test class to check that each time setSelectedBean(...) is called, VGObserver's update(...) is triggered. I used Easy Mock to mock VGObserver interface:

public class AppointmentBeanObservableTest {

private AppointmentFormBean form;
private VGObserver mockObserver;
private final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

@Before
public void setUp() {
    form = new AppointmentFormBean();
    mockObserver = createMock("observer", VGObserver.class);
    form.registerVGObserver(mockObserver);
}

@After
public void tearDown() {
    verify(mockObserver);
}

@Test
public void testIocForVGObserved() throws ParseException {        
    Date aDate = sdf.parse("2015-09-15");
    form.setSelectedDate(aDate);
    mockObserver.update(aDate);
    EasyMock.expectLastCall().anyTimes();
    replay(mockObserver);
}

}

Unfortunately this test royally fails and there was nothing I could find to mend it. Below is the output and project link on github:

Time elapsed: 0.091 sec  <<< FAILURE!
java.lang.AssertionError:   Expectation failure on verify:
observer.update(Tue Sep 15 00:00:00 EDT 2015): expected: at least 1, actual: 0
at org.easymock.internal.MocksControl.verify(MocksControl.java:241)
at org.easymock.EasyMock.verify(EasyMock.java:2100)
at com.vgorcinschi.rimmanew.model.AppointmentBeanObservableTest.tearDown(AppointmentBeanObservableTest.java:40)

Results :
Failed tests: 
 AppointmentBeanObservableTest.tearDown:40 
 Expectation failure on verify:
observer.update(Tue Sep 15 00:00:00 EDT 2015): expected: at least 1, actual: 0
Tests run: 1, Failures: 1, Errors: 0, Skipped: 0

https://github.com/vasigorc/rimmaproject

Any hint is appreciated.


Solution

  • you have it backwards.

    You have to tell the mock what to expect BEFORE you call the actual system under test (SUT). Then after the expectations are set, you make the mock in replay mode. Then finally you call your system under test.

    You called your system under test first, and then set up your mock.

    Changing it to:

     @Test
     public void testIocForVGObserved() throws ParseException {        
         Date aDate = sdf.parse("2015-09-15");
    
         mockObserver.update(aDate);
         EasyMock.expectLastCall().anyTimes();
         replay(mockObserver);
    
         //call SUT now
         form.setSelectedDate(aDate);
    }
    

    Should work.

    You can probably also remove the expectLastCall().anyTimes() line as well. Due to implementation details on how the mock expects calls in record mode, your backwards setup probably set 2 expectations for the call. Once you fix the order of the setup and replay it should only get called once.