Skip to content

TimeSpecimen Cannot be Created for Valid Implementations #111

Open

Description

@Nylle

The following test will throw an exception:

public static class TemporalAdjusterImpl implements TemporalAdjuster {
    private final String value;

    public TemporalAdjusterImpl(String value) {
        this.value = value;
    }

    @Override
    public Temporal adjustInto(Temporal temporal) { return null; }

    public String getValue() { return value; }
}

@Test
void shouldNotThrow() {
    var actual = fixture().create(TemporalAdjusterImpl.class);

    assertThat(actual).isInstanceOf(TemporalAdjusterImpl.class);
    assertThat(actual.getValue()).isNotEmpty();
}

Exception:

com.github.nylle.javafixture.SpecimenException: Unsupported type: class com.github.nylle.javafixture.FixtureTest$TemporalAdjusterImpl

Reason is a mistmatch between detecting the specimen in SpecimenType.isTimeType() and creating an instance in TimeSpecimen.create().

For this particular test, SpecimenType.isTimeType() returns true for any type that implements TemporalAdjuster, but when creating the instance, TimeSpecimen only checks for the specific implementation MonthDay and throws otherwise.

So we either have to relax the TimeSpecimen to return MonthDay for any TemporalAdjuster-implementation, or we make SpecimenType.isTimeType() stricter in detecting temporal types. I'd prefer the latter.

I also assume, the same problem happens for the TemporalAmount-interface while the Temporal-interface seems properly covered.

Activity

  1. Nylle commented on Sep 10, 2024

    @Nylle
    OwnerAuthor

    Here are a few more tests:

    @Test
    void shouldNotThrowEither() {
        var actual = fixture().create(TemporalAdjuster.class);
    
        assertThat(actual).isInstanceOf(MonthDay.class);
    }
    
    @Test
    void thisWorksAndShouldKeepWorking() {
        var actual = fixture().create(MonthDay.class);
    
        assertThat(actual).isInstanceOf(MonthDay.class);
    }
  2. akutschera commented on Jul 6, 2025

    @akutschera
    Collaborator

    When I look at the code, it seems to me that we are doing classes/interfaces from
    java.time.temporal package in an inconsistent way.
    When we create a specimen that implements the Temporal interface, we try to call the "now"
    method on it. This method is not required by the interface (but it works for a lot
    of implementations, e.g. Instant).
    When we create a TemporalAdjuster interface, we fail because this interface is recognized
    as a TimeSpecimen, but it is not assignable to a Temporal interface.
    When we create a TemporalAccessor interface, we treat it like just another interface.
    Therefore creation works without treating it as time related.
    When we create a JapaneseEra, we treat this as a TimeSpecimen and return an actual
    valid JapaneseEra.
    When we create a HijrahChronology, we treat this an ObjectSpecimen and therefore create
    an invalid object (because we fill the private fields with nonsense values).

    I prefer time based objects to behave "normally", i.e. a method that always succeeds
    in valid object of a java.time.* class must always succeed in a JavaFixture created
    object.
    So something like Instant.getNano() should return a non-negative value that is
    less than 999,999,999.

    We could probably achieve this by creating these objects via

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions