mtedone / podam

PODAM - POjo DAta Mocker
https://mtedone.github.io/podam
MIT License
326 stars 749 forks source link

fails to generate collection for kotlin pojo with default parameters #296

Closed vvmuzhichenko closed 2 years ago

vvmuzhichenko commented 2 years ago

Example:

data class Pojo(
    val listValue: List<String>,
    val intValue: Int = 0
)

class PojoTest {
    var factory = PodamFactoryImpl()

    @Test
    fun pojo2() {
        val pojo = factory.manufacturePojoWithFullData(Pojo::class.java)
        Assert.assertEquals(String::class.java, pojo.listValue[0].javaClass)
    }
}

Kotlin compiler creates new constructor for instantiating objects with default parameters and erases generic info for such constructor so constructor. genericParameterTypes returns only Class and not ParameterizedType.

As work around i used special ordering in DataProviderStrategy that prefers constructors with specified ParameterizedType

daivanov commented 2 years ago

Hi,

But if Kotlin erases generic type information, what can Podam do about it?

Thanks, Daniil

vvmuzhichenko commented 2 years ago

As work around i used special ordering in DataProviderStrategy that prefers constructors with specified ParameterizedType

private class FixedDataProviderStrategy(private val strategy: DataProviderStrategy) : DataProviderStrategy by strategy {

    override fun sort(constructors: Array<out Constructor<*>>, order: DataProviderStrategy.Order) {
        strategy.sort(constructors, order)
        // https://github.com/mtedone/podam/issues/296
        Arrays.sort(constructors) { a, b ->
            val aHasTypeInfo = a.genericParameterTypes.find { it is ParameterizedType } != null
            val bHasTypeInfo = b.genericParameterTypes.find { it is ParameterizedType } != null
            bHasTypeInfo.compareTo(aHasTypeInfo)
        }
    }
}
daivanov commented 2 years ago

Yes, I understood workaround, but no one should randomly shuffle constructors to get desired result. This is not generic and sustainable approach. The main question here is "genericParameterTypes returns only Class and not ParameterizedType" is this is a bug in Kotlin compiler stripping generics away or it is somewhere else.

Thanks, Daniil

daivanov commented 2 years ago

This is what Kotlin compiler generates

public final class Pojo {
  @NotNull
  private final List < String > listValue;
  public Pojo(@NotNull List listValue, int intValue) {
    this.listValue = listValue;
    this.intValue = intValue;
  }
  private final int intValue;
  @NotNull public final List < String > getListValue() {
    return this.listValue;
  }
  public final int getIntValue() {
    return this.intValue;
  }

  @NotNull
  public final List < String > component1() {
    return this.listValue;
  }

  public final int component2() {
    return this.intValue;
  }

  @NotNull
  public final Pojo copy(@NotNull List listValue, int intValue) {
    Intrinsics.checkNotNullParameter(listValue, "listValue");
    return new Pojo(listValue, intValue);
  }

  @NotNull
  public String toString() {
    return "Pojo(listValue=" + this.listValue + ", intValue=" + this.intValue + ')';
  }

  public int hashCode() {
    result = this.listValue.hashCode();
    return result * 31 + Integer.hashCode(this.intValue);
  }

  public boolean equals(@Nullable Object other) {
    if (this == other)
      return true;
    if (!(other instanceof Pojo))
      return false;
    Pojo pojo = (Pojo) other;
    return !Intrinsics.areEqual(this.listValue, pojo.listValue) ? false : (!(this.intValue != pojo.intValue));
  }
}
daivanov commented 2 years ago

It was able to keep generics in accessors, but not in the constructor. And there is just one constructor.

Thanks, Daniil

daivanov commented 2 years ago

You should report this issue to Kotlin compiler.

Thanks, Daniil