【问题标题】:AssertJ: generating fluent assertions for Set<A extends B>AssertJ:为 Set<A extends B> 生成流畅的断言
【发布时间】:2019-11-29 16:38:54
【问题描述】:

我偶然发现了 AssertJ 在其中一个断言类中生成以下代码的问题:

public S hasItems(interface ItemInterface... items)

这当然不能编译。

导致问题的示例代码如下:


    public interface EntityInterface {

      Set<? extends ItemInterface> getItems();
    }

    @NoArgsConstructor
    @AllArgsConstructor
    @Data
    @With
    public class EntityA implements EntityInterface {

      private Set<ItemA> items;
    }

    @NoArgsConstructor
    @AllArgsConstructor
    @Data
    @With
    public class EntityA implements EntityInterface {

      private Set<ItemA> items;
    }

    public interface ItemInterface {

      String getName();
    }

    public class ItemA implements ItemInterface {

      public String getName() {
        return "ItemA";
      }
    }

    public class ItemA implements ItemInterface {

      public String getName() {
        return "ItemA";
      }
    }

我已包含导致此错误的最小示例项目,因此可以直接看到。可以从filebin下载

我们正在使用 Lombok 的 @With 注释以及其他考虑因素,并且需要保留接口。

为了解决这个问题,我已经尝试过:

  1. 将 getItems 方法签名更改为:

&lt;T extends ItemInterface&gt; Set&lt;T&gt; getItems();

产生:

public S hasItems(T... items)

但是 T 在上下文中是未知的。

  1. 使用以下方法将界面转换为模板:

public interface EntityInterface&lt;T extends ItemInterface&gt;

这没有任何区别。

有我缺少的解决方案吗?

【问题讨论】:

    标签: java assertj


    【解决方案1】:

    我已经很好地尝试在https://github.com/joel-costigliola/assertj-assertions-generator 中支持泛型,结果发现问题相当复杂,不幸的是,由于缺乏好的解决方案和其他优先事项,我不得不放弃:(。

    我的最佳答案是,生成器只是一种快速获取自定义断言的方法,它并不完美,因此一旦生成就可以根据需要更改它们 - 请阅读 http://joel-costigliola.github.io/assertj/assertj-assertions-generator.html#philosophy

    我希望我的回答不会太令人失望。

    【讨论】:

    • 感谢您的回答!很遗憾,但模板确实会使事情变得相当复杂。我决定将接口添加到排除列表并解决它。
    • 很高兴知道!我通常倾向于修改生成的断言以使它们更面向领域。所以生成器确实是一种快速获取我稍后会调整的断言的方法。
    【解决方案2】:

    正如您所观察到的,问题在于 AssertJ 使用无效方法创建了 AbstractEntityInterfaceAssert 类,例如:

    public S hasItems(interface ItemInterface... items) {/*...*/}
    

    我没有使用 AssertJ 的实际经验,但经过一些研究,我找到了 2 个保留编译时类型安全性的解决方法(将 EntityInterface.getItems() 方法更改为返回 Set&lt;?&gt; 有效,但不可接受):

    1. 使用实现接口的抽象类,而不是直接使用接口:
    public interface ItemInterface {
      String getName();
    }
    
    public abstract class AbstractItem implements ItemInterface {
    }
    
    public class ItemA extends AbstractItem {
      public String getName() {
        return "ItemA";
      }
    }
    
    // ItemB same as ItemA
    
    public interface EntityInterface {
      Set<? extends AbstractItem> getItems();
    }
    
    @NoArgsConstructor
    @AllArgsConstructor
    @Data
    @With
    public class EntityA implements EntityInterface {
      private Set<ItemA> items;
    }
    
    // EntityB same as EntityA, but with Set of ItemB
    

    可以看出,与您的示例相比,唯一的变化是AbstractItem 用作基类,而不是在ItemAItemAEntityInterface.getItems() 中都实现ItemInterfaceEntityInterface.getItems() 方法更改为返回@ 987654337@ 而不是 Set&lt;? extends ItemInterface&gt;

    通过这些更改,程序可以正确编译并且生成的AbstractEntityInterfaceAssert 类具有有效的方法签名,例如:

    public S hasItems(AbstractItem... items) { /*...*/ }
    
    1. 第二种解决方法是使用assertj-assertions-generator-maven-plugin settings 从生成中排除EntityInterface
        <build>
            <plugins>
                <plugin>
                    <groupId>org.assertj</groupId>
                    <artifactId>assertj-assertions-generator-maven-plugin</artifactId>
                    <!-- ... -->
                    <configuration>
                        <!-- ... -->
                        <!-- Exclude classes matching the regex from generation -->
                        <excludes>
                            <param>com.example.EntityInterface</param>
                        </excludes>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    

    以上配置阻止AbstractEntityInterfaceAssert.java的生成。

    我不知道这些变通办法是否适用于您的用例,遗憾的是我无法提供更好的解决方案或解释(这是 AspectJ 的错误还是限制?)。最好的人选是Joel Costigliola - AssertJ

    的作者

    有用的读物​​:

    【讨论】:

    • 感谢您的广泛回答!最后我选择了第二个选项,因为项目之间的相似性不足以让抽象类在语义上有意义。缺点是,AssertJ 方法在处理接口时不可用,但也许这是最好的 :)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-10-21
    • 1970-01-01
    • 1970-01-01
    • 2023-03-05
    • 2010-09-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多