Search code examples
javatestingjavafxspockparameterization

Test for parameterisation of a class


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>{
}

Solution

  • 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>