(上下文:我在基于属性的测试方面的背景主要来自scala的scalacheck库,jqwik中某些类型和注释的使用感觉有点不同,还有几个范例我还不太了解。)
我不知道如何最好地组合现有的原语类型的Arbitrary
定义,以产生一个可重用的任意(多个@Property
或测试类所需的)任意的,这些任意构建在我定义的其他Aribtrary
定义之上。
考虑到这一点可能要清楚得多,举个例子:
// assume this has a builder pattern or all-args constructor.
// consider this is some sort of core domain object that I need in various
// test suites
public class MyComplexClass {
private final String id; // positive-integer shaped
private final String recordId; // uuid-shaped
private final String creatorId; // positive-integer shaped
private final String editorId; // positive-integer shaped
private final String nonce; // uuid-shaped
private final String payload; // random string
}
我的直觉是定义生成UUID类字符串的Aribrary<String>
,以及生成正整数字符串的另一个字符串,类似于:
public class MyArbitraries {
public Arbitrary<String> arbUuidString() {
return Combinators.combine(
Arbitraries.longs(), Arbitraries.longs(), Arbitraries.of(Set.of('8', '9', 'a', 'b')))
.as((l1, l2, y) -> {
StringBuilder b = new StringBuilder(new UUID(l1, l2).toString());
b.setCharAt(14, '4');
b.setCharAt(19, y);
return UUID.fromString(b.toString());
});
}
public Arbitrary<String> arbNumericIdString() {
return Arbitraries.shorts().map(Math::abs).map(i -> "" + i);
}
}
但是,我不确定如何最好地利用这些方法来生成Arbitrary< MyComplexClass>
。我想要这样的东西:
public class MyDomain extends DomainContextBase {
@Provider
public Arbitrary<MyComplexClass> arbMyComplexClass() {
return Builders.withBuilder(MyComplexClass::newBuilder)
// best way to reference these?!
.use(arbNumericIdString()).in(MyComplexClass.Builder::setId)
.use(arbUuidString()).in(MyComplexClass.Builder::setCreatorId)
// etc.
.build(MyComplexClass.Builder::build);
}
}
我在这里的理解是:
@ForAll
‘注入’或提供这些仲裁器,因为只有在@Property
-annotated方法ForAll
。由于类似的原因,在这里不能使用@Domain
,原因类似:H 117
我不能真正使用ArbitrarySupplier
或类似于这里没有明显的“类型”,它主要是一串字符串<H 219F 220
创建静态Arbitrary<String>
函数并直接调用它们是最好的选择吗?
发布于 2022-11-23 08:34:56
一个最初的评论是:@ForAll
也适用于带有@Provide
注解的方法和域。下面是一个简单的例子:
class MyDomain extends DomainContextBase {
@Provide
public Arbitrary<String> strings(@ForAll("lengths") int length) {
return Arbitraries.strings().alpha().ofLength(length);
}
@Provide
public Arbitrary<Integer> lengths() {
return Arbitraries.integers().between(3, 10);
}
// Will not be used by strings() method
@Provide
public Arbitrary<Integer> negatives() {
return Arbitraries.integers().between(-100, -10);
}
}
class MyProperties {
@Property(tries = 101)
@Domain(MyDomain.class)
public void printOutAlphaStringsWithLength3to10(@ForAll String stringsFromDomain) {
System.out.println(stringsFromDomain);
}
}
可能令人困惑的是,@ForAll("myString")
中的字符串引用只在本地计算(类本身、超类和包含类)。为了防止基于字符串的引用魔术,这是有目的的;首先必须回到字符串中--因为Java注释中不能使用“方法参考”--这已经够糟糕的了。
至于你的具体问题:
是创建静态任意函数并直接调用它们的最佳选择吗?
我认为这是一种“足够好”的方法,用于在单个域中或当您有几个从公共超类继承的相关域时共享生成器。
当您想要在不相关的域之间共享生成器时,您必须这样做:
type.
RecordId
、UUIDString
等事物引入值类型。然后您可以使用域(或注册的ArbitraryProvider
s )来生成基于注释的不同类型的变体。然后,可以在provider方法或任意提供程序中检查注释。下面是一个例子:class MyNumbers extends DomainContextBase {
@Provide
Arbitrary<Integer> numbers() {
return Arbitraries.integers().between(0, 255);
}
}
@Domain(MyNumbers.class)
class MyDomain extends DomainContextBase {
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@interface Name {}
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@interface HexNumber {}
@Provide
public Arbitrary<String> names(TypeUsage targetType) {
if (targetType.isAnnotated(Name.class)) {
return Arbitraries.strings().alpha().ofLength(5);
}
return null;
}
@Provide
public Arbitrary<String> numbers(TypeUsage targetType) {
if (targetType.isAnnotated(HexNumber.class)) {
return Arbitraries.defaultFor(Integer.class).map(Integer::toHexString);
}
return null;
}
}
@Property(tries = 101)
@Domain(MyDomain.class)
public void generateNamesAndHexNumbers(
@ForAll @MyDomain.Name String aName,
@ForAll @MyDomain.HexNumber String aHexNumber
) {
System.out.println(aName);
System.out.println(aHexNumber);
}
此示例还展示了如何通过注释域实现类并具有参数注入或使用MyNumbers
在另一个域(MyDomain
)中使用一个域(MyDomain
)。
但是,对于共享jqwik中缺少的仲裁,可能有一个有用的特性。jqwik团队对任何建议都很满意。
https://stackoverflow.com/questions/74537664
复制相似问题