0

In my test I have this:

given:
def dueDateCol = App.instance.fxmlController.treeTableView.columns.get( 1 )

when:
def editorCell = dueDateCol.cellFactory.call( dueDateCol )

then:
editorCell instanceof TreeTableCell<TaskItem, LocalDate>

My app code is currently this:

class DueDateEditor extends TreeTableCell {
}

... but the test passes! I want it to fail until the app code is correctly parameterised:

class DueDateEditor extends TreeTableCell<TaskItem, LocalDate>{
}
mike rodent
  • 14,126
  • 11
  • 103
  • 157
  • Does this [help](https://stackoverflow.com/questions/48263921/generics-with-spock-stub)? – Miss Chanandler Bong Apr 04 '20 at 17:20
  • I'm not sure! That seems to be an entirely different thing, as far as I can make out. Of course the comment about Java type erasure at run time may be the answer to this, for all I know... – mike rodent Apr 04 '20 at 17:47

1 Answers1

1

Again, like in my other answer, a general remark: Why would you test such a thing? And if you test it, I would rather make sure to somehow integrate static type checks into the compilation (or post-compilation) process. Ideally, the compiler should throw an error if a contract like this is violated.

Anyway, for what it is worth, you could do this (both application and test classes are logical continuations of my MCVE in the other answer):

package de.scrum_master.stackoverflow.q61032514;

public class TaskItem {}
package de.scrum_master.stackoverflow.q61032514;

public class TreeTableCell<A, B> {}
package de.scrum_master.stackoverflow.q61032514;

import java.time.LocalDate;

public class DueDateEditor extends TreeTableCell<TaskItem, LocalDate> {
  String text;

  public boolean isEmpty() {
    return text == null || text.trim() == "";
  }

  public void startEdit() {
    if (!isEmpty())
      callSuperStartEdit();
  }

  public void callSuperStartEdit() {}
}

Now the following test passes:

package de.scrum_master.stackoverflow.q61032514

import spock.lang.Specification

import java.lang.reflect.ParameterizedType
import java.time.LocalDate

class DueDateEditorTest extends Specification {
  def "test generic type arguments for DueDateEditor"() {
    given:
    def superclass = DueDateEditor.superclass
    def genericSuperclass = DueDateEditor.genericSuperclass

    expect:
    superclass == TreeTableCell
    genericSuperclass instanceof ParameterizedType
    genericSuperclass.actualTypeArguments == [TaskItem, LocalDate]
    // Or, if you want to avoid the underlined 'actualTypeArguments' in your IDE:
    // (genericSuperclass as ParameterizedType).actualTypeArguments == [TaskItem, LocalDate]
  }
}

If you change the class under test to DueDateEditor extends TreeTableCell, the test fails like this:

Condition not satisfied:

genericSuperclass instanceof ParameterizedType
|                 |          |
|                 false      interface java.lang.reflect.ParameterizedType
class de.scrum_master.stackoverflow.q61032514.TreeTableCell (java.lang.Class)

If you change to something like DueDateEditor extends TreeTableCell<String, LocalDate>, it fails like this:

genericSuperclass.actualTypeArguments == [TaskItem, LocalDate]
|                 |                   |   |         |
|                 |                   |   |         class java.time.LocalDate
|                 |                   |   class de.scrum_master.stackoverflow.q61032514.TaskItem
|                 |                   false
|                 [<java.lang.Class@192d43ce cachedConstructor=null newInstanceCallerCache=null name=java.lang.String reflectionData=java.lang.ref.SoftReference@54709809 classRedefinedCount=0 genericInfo=sun.reflect.generics.repository.ClassRepository@2a2da905 enumConstants=null enumConstantDirectory=null annotationData=java.lang.Class$AnnotationData@24f360b2 annotationType=null classValueMap=[java.lang.ClassValue$Identity@39c0f4a:java.lang.ClassValue$Entry@60cf80e7]>, <java.lang.Class@38e79ae3 cachedConstructor=null newInstanceCallerCache=null name=java.time.LocalDate reflectionData=java.lang.ref.SoftReference@302fec27 classRedefinedCount=0 genericInfo=sun.reflect.generics.repository.ClassRepository@770d0ea6 enumConstants=null enumConstantDirectory=null annotationData=null annotationType=null classValueMap=[java.lang.ClassValue$Identity@39c0f4a:java.lang.ClassValue$Entry@48c40605]>]
de.scrum_master.stackoverflow.q61032514.TreeTableCell<java.lang.String, java.time.LocalDate>

Or if you want to better compare lists in the error message, you could use toList() like this:

    genericSuperclass.actualTypeArguments.toList() == [TaskItem, LocalDate]
    // Or, if you want to avoid the underlined 'actualTypeArguments' in your IDE:
    //(genericSuperclass as ParameterizedType).actualTypeArguments.toList() == [TaskItem, LocalDate]

Then the last error message would change to:

Condition not satisfied:

genericSuperclass.actualTypeArguments.toList() == [TaskItem, LocalDate]
|                 |                   |        |   |         |
|                 |                   |        |   |         class java.time.LocalDate
|                 |                   |        |   class de.scrum_master.stackoverflow.q61032514.TaskItem
|                 |                   |        false
|                 |                   [class java.lang.String, class java.time.LocalDate]
|                 [<java.lang.Class@192d43ce cachedConstructor=null newInstanceCallerCache=null name=java.lang.String reflectionData=java.lang.ref.SoftReference@60cf80e7 classRedefinedCount=0 genericInfo=sun.reflect.generics.repository.ClassRepository@302fec27 enumConstants=null enumConstantDirectory=null annotationData=java.lang.Class$AnnotationData@770d0ea6 annotationType=null classValueMap=[java.lang.ClassValue$Identity@39c0f4a:java.lang.ClassValue$Entry@48c40605]>, <java.lang.Class@2d2ffcb7 cachedConstructor=null newInstanceCallerCache=null name=java.time.LocalDate reflectionData=java.lang.ref.SoftReference@54107f42 classRedefinedCount=0 genericInfo=sun.reflect.generics.repository.ClassRepository@1b11ef33 enumConstants=null enumConstantDirectory=null annotationData=null annotationType=null classValueMap=[java.lang.ClassValue$Identity@39c0f4a:java.lang.ClassValue$Entry@476aac9]>]
de.scrum_master.stackoverflow.q61032514.TreeTableCell<java.lang.String, java.time.LocalDate>
kriegaex
  • 63,017
  • 15
  • 111
  • 202
  • Thanks. Why would I test such a thing? Yes, you're absolutely right, and I had a feeling that this might be a dodgy test. There is a method in `TreeTableCell`, here `updateItem( LocalDate dueDate, boolean empty )`, and just giving that the annotation `@Override` is sufficient to ensure, at compile time I believe, that you have to make the 2nd parameter of `TreeTableCell` `LocalDate`. – mike rodent Apr 05 '20 at 07:53