Uploaded image for project: 'Spring Framework'
  1. Spring Framework
  2. SPR-15692

Kotlin unable to inherit type for WebTestClient#BodySpec

    Details

    • Type: Improvement
    • Status: Closed
    • Priority: Minor
    • Resolution: Complete
    • Affects Version/s: 5.0 RC2
    • Fix Version/s: 5.0.6, 5.1 RC1
    • Component/s: Test, Web
    • Labels:

      Description

      It seems that due to recursive generics in BodySpec interface

      interface BodySpec<B, S extends BodySpec<B, S>>
      

      and due to expectBody method returns

      <B> BodySpec<B, ?> expectBody(Class<B> bodyType);
      

      WebTestClient cannot be used in Kotlin.

      Kotlin inherits the result of .expectBody(Person::class.java) as BodySpec<Person, *> and thus the following methods in chain cannot be constructed due to the following error:

      Error:(25, 20) Kotlin: Type inference failed: Not enough information to infer parameter T in fun <T : Nothing!> isEqualTo(p0: Controller.Person!): T!
      Please specify it explicitly.

      And it applies only Nothing as a type parameter.
      But in this case generated bytecode contains the following line

      throw null
      

      Example:

          @Test
          fun `test get`() {
              val expectBody: BodySpec<Person, *> = client.get().uri("/person/42").exchange()
                      .expectBody(Person::class.java)
              expectBody.isEqualTo(Person("42", "Ivan"))                            // doesn't compile here
              expectBody.isEqualTo<BodySpec<Person, *>>(Person("42", "Ivan"))       // doesn't compile here
              expectBody.isEqualTo<Nothing>(Person("42", "Ivan"))                   // compile but lead to "throw null" in bytecode
          }
      

      If you work with list the situation is a bit better - Kotlin still cannot inherit type param automatically but you can specify it explicitly due to method expectBodyList in interface ListBodySpec doesn't return wildcards

      <E> ListBodySpec<E> expectBodyList(Class<E> elementType);
      

      Example:

      @Test
          fun `test list`() {
              val expectBodyList: ListBodySpec<Person> = client.get().uri("/person").exchange()
                      .expectBodyList(Person::class.java)
              expectBodyList.consumeWith<ListBodySpec<Person>> { list -> Assert.assertTrue(true) }   // need to specify type param explicitly
          }
      

      Full example with java and kotlin can be found here.
      Tests in java works well in these cases.

        Attachments

          Issue Links

            Activity

              People

              • Assignee:
                sdeleuze Sébastien Deleuze
                Reporter:
                mskonovalov Mikhail Konovalov
                Last updater:
                Stéphane Nicoll
              • Votes:
                0 Vote for this issue
                Watchers:
                5 Start watching this issue

                Dates

                • Created:
                  Updated:
                  Resolved:
                  Days since last comment:
                  34 weeks, 5 days ago