AssertJ:为 Set<A extends B> 生成流畅的断言
AssertJ: generating fluent assertions for Set<A extends B>
我偶然发现了一个问题,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 注释以及其他考虑因素,需要保留接口。
为了解决这个问题,我试过:
- 将 getItems 方法签名更改为:
<T extends ItemInterface> Set<T> getItems();
产生:
public S hasItems(T... items)
然而 T 在上下文中是未知的。
- 使用以下方法将界面变成模板:
public interface EntityInterface<T extends ItemInterface>
这没有任何区别。
是否有我遗漏的解决方案?
如您所见,问题在于 AssertJ 使用无效方法创建 AbstractEntityInterfaceAssert
class,例如:
public S hasItems(interface ItemInterface... items) {/*...*/}
我没有使用 AssertJ 的实际经验,但经过一些研究后,我找到了 2 个保留编译时类型安全的解决方法(将 EntityInterface.getItems()
方法更改为 return Set<?>
有效,但不可接受):
- 使用实现接口的抽象class,而不是直接使用接口:
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
用作基础 class 而不是在 ItemA
和 [ 中实现 ItemInterface
=19=] 和 EntityInterface.getItems()
方法更改为 return Set<? extends AbstractItem>
而不是 Set<? extends ItemInterface>
。
通过这些更改,程序可以正确编译并生成 AbstractEntityInterfaceAssert
class 具有有效的方法签名,例如:
public S hasItems(AbstractItem... items) { /*...*/ }
- 第二种解决方法是使用
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
的作者
有用的读物:
contains
check on a Collection gives compiler error
- AssertJ
containsExactly
assertion on list with wildcard
我曾在 https://github.com/joel-costigliola/assertj-assertions-generator 中尝试支持泛型,结果发现问题相当复杂,不幸的是,由于缺乏好的解决方案和其他优先事项,我不得不放弃 :(。
我的最佳答案是,生成器只是一种快速获取自定义断言的方法,它并不意味着完美,因此一旦生成,请根据您的需要进行更改 - 阅读 http://joel-costigliola.github.io/assertj/assertj-assertions-generator.html#philosophy。
希望我的回答不会太令人失望。
我偶然发现了一个问题,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 注释以及其他考虑因素,需要保留接口。
为了解决这个问题,我试过:
- 将 getItems 方法签名更改为:
<T extends ItemInterface> Set<T> getItems();
产生:
public S hasItems(T... items)
然而 T 在上下文中是未知的。
- 使用以下方法将界面变成模板:
public interface EntityInterface<T extends ItemInterface>
这没有任何区别。
是否有我遗漏的解决方案?
如您所见,问题在于 AssertJ 使用无效方法创建 AbstractEntityInterfaceAssert
class,例如:
public S hasItems(interface ItemInterface... items) {/*...*/}
我没有使用 AssertJ 的实际经验,但经过一些研究后,我找到了 2 个保留编译时类型安全的解决方法(将 EntityInterface.getItems()
方法更改为 return Set<?>
有效,但不可接受):
- 使用实现接口的抽象class,而不是直接使用接口:
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
用作基础 class 而不是在 ItemA
和 [ 中实现 ItemInterface
=19=] 和 EntityInterface.getItems()
方法更改为 return Set<? extends AbstractItem>
而不是 Set<? extends ItemInterface>
。
通过这些更改,程序可以正确编译并生成 AbstractEntityInterfaceAssert
class 具有有效的方法签名,例如:
public S hasItems(AbstractItem... items) { /*...*/ }
- 第二种解决方法是使用
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
的作者有用的读物:
contains
check on a Collection gives compiler error- AssertJ
containsExactly
assertion on list with wildcard
我曾在 https://github.com/joel-costigliola/assertj-assertions-generator 中尝试支持泛型,结果发现问题相当复杂,不幸的是,由于缺乏好的解决方案和其他优先事项,我不得不放弃 :(。
我的最佳答案是,生成器只是一种快速获取自定义断言的方法,它并不意味着完美,因此一旦生成,请根据您的需要进行更改 - 阅读 http://joel-costigliola.github.io/assertj/assertj-assertions-generator.html#philosophy。
希望我的回答不会太令人失望。