google / gwtmockito

Better GWT unit testing
https://google.github.io/gwtmockito
Apache License 2.0
157 stars 51 forks source link

Mocked com.google.gwt.i18n.client.DateTimeFormatInfo in com.google.gwt.i18n.shared.DateTimeFormat return nothing on ampms() method call #30

Closed redboul closed 10 years ago

redboul commented 10 years ago

Hi! I'm trying to set up my test cases with GWT Mockito but I'm encountering issues with GWT date formatting : when calling DateTimeFormat formatter = DateTimeFormat.getFormat(format); with format "MM/dd/yyyy h:mm a" and then trying to parse the date 'Tue Jan 04 12:25:00 CET 2011' we are getting the following Exception: java.lang.ArrayIndexOutOfBoundsException: 1 at com.google.gwt.i18n.shared.DateTimeFormat.formatAmPm(DateTimeFormat.java:1032) at com.google.gwt.i18n.shared.DateTimeFormat.subFormat(DateTimeFormat.java:1760) at com.google.gwt.i18n.shared.DateTimeFormat.format(DateTimeFormat.java:810) at com.google.gwt.i18n.shared.DateTimeFormat.format(DateTimeFormat.java:746)

Is this a bug or is there a way to control the behaviour of the injected DateTimeFormatInfo (which is a mock injected by GWTMockito I suppose)

Thanks !

ekuefler commented 10 years ago

Looks like GwtMockito is missing some formatting strings. I've fixed this in c5465caa12ac51ff93294d3d8233f1e2584db9df and uploaded a snapshot; if you pull down the latest version it should work. You can also override the mock injected by GwtMockito to make it work yourself at the current version by doing this:

@GwtMock LocaleInfoImpl localeInfo;

@Before public void fixDateTimeRendering() {
  com.google.gwt.i18n.client.DateTimeFormatInfo dtfi = mock(com.google.gwt.i18n.client.DateTimeFormatInfo.class);
  when(localeInfo.getDateTimeFormatInfo()).thenReturn(dtfi);
  when(dtfi.ampms()).thenReturn(new String[] {"AM", "PM"});
}

Thanks for the report, and let me know if this doesn't fix you.

redboul commented 10 years ago

Great thanks, it worked ! BTW, didn't know we could simply override the gwtmock in the @before method. I do also have a question, when we have multiple times an element in a GWT component (like multiple SpanElement), it seems that gwtmockito uses the same mock for every instance of the element. Is it normal or am I missing something?

Thanks anyway for the fix

ekuefler commented 10 years ago

Under normal circumstances GwtMockito will return a new mock every time GWT.create is called: https://github.com/google/gwtmockito/blob/master/gwtmockito/src/main/java/com/google/gwtmockito/GwtMockito.java#L314. If you've declared a field using @GwtMock, it will always be that same field that's returned. Does this match what you're seeing, or is something going wrong somewhere?

redboul commented 10 years ago

mm, I don't know if I made myself clear enough... I'm having a class that looks like this:

public class HumanTaskMetadataView extends Composite {

    @UiField(provided = true)
    MetadataMessages messages = new MetadataMessages();

    @UiField(provided = true)
    IHumanTaskItem task;

    @UiField
    AnchorElement caseId;

    @UiField
    SpanElement priority;

    @UiField
    SpanElement assignedTo;

    @UiField
    DivElement doneByContainer;

    @UiField
    SpanElement doneBy;

    @UiField
    SpanElement dueDate;

    @UiField
    SpanElement lastUpdateDate;

    @UiField
    LabelElement labelDoneOn;

    @UiField
    SpanElement assignedDate;

    @UiField
    ParagraphElement description;

    interface Binder extends UiBinder<HTMLPanel, HumanTaskMetadataView> {
    }

    protected static Binder binder = GWT.create(Binder.class);

    public HumanTaskMetadataView(final IHumanTaskItem task) {
        this.task = task;
        initWidget(binder.createAndBindUi(this));
        //some other stuff with the fields
        //....
   }
}

here is my test case :

@RunWith(GwtMockitoTestRunner.class)
public class HumanTaskMetadataViewTest {

    @GwtMock
    SpanElement doneBy;

   @GwtMock
    SpanElement dueDate;

    //other @GWTMock Fields
    //...

 @Test
    public void testHumanTaskMetadataViewShouldOnlyHaveAssignedUserAndLastUpdate() throws Exception {
        IHumanTaskItem humanTaskItem = mock(IHumanTaskItem.class);
        HumanTaskMetadataView humanTaskMetadataView = new HumanTaskMetadataView(humanTaskItem);
       //test stuff
       }
}

It seems that both doneBy and dueDate are the same mock object whereas they are not in real Gwt binding. Thus, I was wondering if this was normal...

ekuefler commented 10 years ago

Ah I see. Yeah, I suspect in that case you should be getting this exception, is that what you're seeing?

GwtMockito doesn't know which GwtMock corresponds to which element if they're both of the same type (it's not smart enough to match the names), so it only allows you to define one GwtMock. What you can do in the common case is just access the field directly in your test - this works since @UiFields have to be declared package-private anyways (assuming your test is in the same package as your class):

    @Test
    public void testHumanTaskMetadataViewShouldOnlyHaveAssignedUserAndLastUpdate() throws Exception {
        IHumanTaskItem humanTaskItem = mock(IHumanTaskItem.class);
        HumanTaskMetadataView humanTaskMetadataView = new HumanTaskMetadataView(humanTaskItem);
        when(humanTaskMetadataView.doneBy.getText()).thenReturn("doneBy"); // <------
       }

It's pretty rare that you have to define GwtMocks yourself - usually only when you're calling GWT.create directly. Will this work for you?

redboul commented 10 years ago

Great! sorry I didn't simply thought of calling field from the HumanTaskMetadataView class directly T_T By the way, I did have the exception thrown when having multiple @GwtMock instances on a same class, but, at first glance to GWT (I was taking over an existing GWT project), I was a bit lost with the GWT.create & UiBinder magic and tried some 'outlaw' things... ; ) Thanks you very much for your time helping me anyway