对Java中的自定义有序列表进行基于属性的测试

鉴于以下订购要求:

所有以“ foo”开头的字符串都应放在首位。

所有以“ bar”开头的字符串都应为最后。

以“ foo”或“ bar”开头的字符串也可以出现在列表中。

如何能够使用基于属性的测试来测试上述要求的实现而不会感到头疼?

是否有比以下内容更优雅的东西:

List<String> strings = Arrays.asList("foo","bar","bar1","jar");
Collections.shuffle(strings);
assertListStartWith(strings,"foo");
assertListEndsWith(strings,"bar1");
assertThat(strings,hasItem( "jar"));
iCMS 回答:对Java中的自定义有序列表进行基于属性的测试

我假设您具有带签名的分类功能

List<String> sortFooBar(List<String> list)

我看到sortFooBar(list)应该满足的至少五个属性:

  1. 保留列表中的所有项目-仅保留这些项目
  2. 第一个“ foo”之前没有项目
  3. 第一个和最后一个“ foo”之间没有其他项
  4. 最后一个“栏”之后没有项目
  5. 第一个和最后一个“小节”之间没有其他项目

在一种实际的功能语言中,这些属性都非常容易用Java来表述,这需要一些代码。因此,这是我对使用jqwik作为PBT框架并使用AssertJ进行断言的问题的看法:

import java.util.*;
import java.util.function.*;
import org.assertj.core.api.*;
import net.jqwik.api.*;

class MySorterProperties {
    
    @Property
    void allItemsAreKept(@ForAll List<@From("withFooBars") String> list) {
        List<String> sorted = MySorter.sortFooBar(list);
        Assertions.assertThat(sorted).containsExactlyInAnyOrderElementsOf(list);
    }
    
    @Property
    void noItemBeforeFoo(@ForAll List<@From("withFooBars") String> list) {
        List<String> sorted = MySorter.sortFooBar(list);
        int firstFoo = findFirst(sorted,item -> item.startsWith("foo"));
        if (firstFoo < 0) return;
        Assertions.assertThat(sorted.stream().limit(firstFoo)).isEmpty();
    }
    
    @Property
    void noItemBetweenFoos(@ForAll List<@From("withFooBars") String> list) {
        List<String> sorted = MySorter.sortFooBar(list);
        int firstFoo = findFirst(sorted,item -> item.startsWith("foo"));
        int lastFoo = findLast(sorted,item -> item.startsWith("foo"));
        if (firstFoo < 0 && lastFoo < 0) return;
        List<String> allFoos = sorted.subList(
            Math.max(firstFoo,0),lastFoo >= 0 ? lastFoo + 1 : sorted.size()
        );
        Assertions.assertThat(allFoos).allMatch(item -> item.startsWith("foo"));
    }
    
    @Property
    void noItemAfterBar(@ForAll List<@From("withFooBars") String> list) {
        List<String> sorted = MySorter.sortFooBar(list);
        int lastBar = findLast(sorted,item -> item.startsWith("bar"));
        if (lastBar < 0) return;
        Assertions.assertThat(sorted.stream().skip(lastBar + 1)).isEmpty();
    }
    
    @Property
    void noItemBetweenBars(@ForAll List<@From("withFooBars") String> list) {
        List<String> sorted = MySorter.sortFooBar(list);
        int firstBar = findFirst(sorted,item -> item.startsWith("bar"));
        int lastBar = findLast(sorted,item -> item.startsWith("bar"));
        if (firstBar < 0 && lastBar < 0) return;
        List<String> allFoos = sorted.subList(
            Math.max(firstBar,lastBar >= 0 ? lastBar + 1 : sorted.size()
        );
        Assertions.assertThat(allFoos).allMatch(item -> item.startsWith("bar"));
    }
    
    @Provide
    Arbitrary<String> withFooBars() {
        Arbitrary<String> postFix = Arbitraries.strings().alpha().ofMaxLength(10);
        return Arbitraries.oneOf(
            postFix,postFix.map(post -> "foo" + post),postFix.map(post -> "bar" + post)
        );
    }
    
    int findFirst(List<String> list,Predicate<String> condition) {
        for (int i = 0; i < list.size(); i++) {
            String item = list.get(i);
            if (condition.test(item)) {
                return i;
            }
        }
        return -1;
    }
    
    int findLast(List<String> list,Predicate<String> condition) {
        for (int i = list.size() - 1; i >= 0; i--) {
            String item = list.get(i);
            if (condition.test(item)) {
                return i;
            }
        }
        return -1;
    }
}

这是与规范一致的天真的实现:

class MySorter {
    static List<String> sortFooBar(List<String> in) {
        ArrayList<String> result = new ArrayList<>();
        int countFoos = 0;
        for (String item : in) {
            if (item.startsWith("foo")) {
                result.add(0,item);
                countFoos++;
            } else if (item.startsWith("bar")) {
                result.add(result.size(),item);
            } else {
                result.add(countFoos,item);
            }
        }
        return result;
    }
}

在此示例中,属性的代码超出了实现的代码量。这可能是好事,也可能是坏事,具体取决于所需行为的技巧。

本文链接:https://www.f2er.com/2038335.html

大家都在问