【问题标题】:How do you assert that a `Collection` of `Collection`s contains elements containing certain elements?您如何断言 `Collection`s 的`Collection` 包含包含某些元素的元素?
【发布时间】:2019-08-26 09:19:09
【问题描述】:

假设我有 Strings "foo""bar"baz

我收到了Stream<String[]> candidates

我现在想 assertThatcandidates 中的所有元素都是包含 {"foo", "bar"}(任意顺序)或 {"bar", "baz"}(任意顺序)的元组。

我怎样才能最好地做到这一点?

【问题讨论】:

    标签: java unit-testing junit hamcrest assertj


    【解决方案1】:

    您可以使用allMatchallSatisfy 之类的东西

    assertThat(candidates).allMatch(candidate -> candidate.contains...)
    

    assertThat(candidates).allSatisfy(
              candidate -> assertThat(candidate).satisfiesAnyOf(
                           c -> {      
                              c.contains("foo");
                              c.contains("bar");
                           },
                           c -> {      
                              c.contains("bar");
                              c.contains("baz");
                           })                                     
             );
    

    【讨论】:

      【解决方案2】:

      为简单起见,我将创建一个可以验证元组的函数。将对流中的每个元素调用此函数。我还将创建第二个函数,它可以检查一个数组是否包含另一个数组的每个元素。像这样的:

      import org.junit.Test;
      import java.util.stream.Stream;
      import static org.assertj.core.api.Assertions.assertThat;
      
      public class MyTest {
      
          @Test
          public void test(){
              Stream<String[]> stream = Stream.of(new String[]{"foo", "bar"}, new String[]{"bar", "baz"});
              stream.forEach(arr -> assertThat(validTuple(arr)).isTrue());
      
              //Order does not matter:
              stream = Stream.of(new String[]{"bar", "foo"}, new String[]{"baz", "bar"});
              stream.forEach(arr -> assertThat(validTuple(arr)).isTrue());
      
              //Invalid tuples
              stream = Stream.of(new String[]{"foo", "asdf"}, new String[]{"foo", "baz"});
              stream.forEach(arr -> assertThat(validTuple(arr)).isFalse());
          }
      
          private boolean validTuple(String[] array){
              if(array.length != 2) return false;
              return containsAll(array, "foo", "bar") || containsAll(array, "bar", "baz");
          }
      
          private boolean containsAll(String[] array, String... values){
              for (String value : values) {
                  boolean containsValue = false;
                  for (String s : array) {
                      if(s.equals(value)){
                          containsValue = true;
                          break;
                      }
                  }
                  if(!containsValue){
                      return false;
                  }
              }
      
              return true;
          }
      
      }
      

      您现在可以轻松地将这些方法提取到一个专用类中,以便在需要时在其他测试中重复使用它们。

      【讨论】:

      • 如果我们要走这条路,我建议将您的函数设为Predicate 并使用stream.allMatch(validTuple)
      【解决方案3】:

      第一步是使用Stream#allMatch(Predicate) 检查元组中[foo, bar] [bar, baz] 和元组大小的组合。

      在第二步中,我已将内部数组转换为 Set&lt;String&gt;,这样可以轻松解决 [foo, foo] 之类的情况,因为重复项将被删除,而且我们可以使用 Set.contains() 进行 O(1) 查找检查[foo, bar] [bar, baz] 是否存在于元组流中:

      class TupleTest {
      
      @org.junit.jupiter.api.Test
      void testTupleOfCandidates_True() {
          Stream<String[]> candidates = Stream.of(new String[]{"bar", "foo"}, new String[]{"bar", "baz"});
          assertTrue(isCandidateStreamValid(candidates));
      
          candidates = Stream.of(new String[]{"bar", "foo"}, new String[]{"bar", "baz"});
          assertTrue(isCandidateStreamValid(candidates));
      
      }
      
      @org.junit.jupiter.api.Test
      void testTupleOfCandidates_False() {
          Stream<String[]> candidates = Stream.of(new String[]{"foo", "foo"}, new String[]{"bar", "baz"});
          assertFalse(isCandidateStreamValid(candidates));
      
          candidates = Stream.of(new String[]{"bar", "foo"}, new String[]{"bar", "baz"}, new String[]{"baz", "bar"});
          assertTrue(isCandidateStreamValid(candidates));
      }
      
      public boolean isCandidateStreamValid(Stream<String[]> candidates){
              return candidates.allMatch(arr -> {
                  Set<String> data = new HashSet(Arrays.asList(arr));
                  return data.size() == 2
                          &&
                          ((data.contains("foo") && data.contains("bar"))
                                  ||
                                  (data.contains("bar") && data.contains("baz")));
              });
          }
      }
      

      【讨论】:

      • 这会失败,比如{"foo", "foo"}。 (顺便说一句,您还忘记检查 arr 的大小为 2。)
      • @User1291 感谢您发现这一点,我修复了之前发布的代码,现在它应该适用于所有情况。
      猜你喜欢
      • 2018-04-28
      • 2017-12-02
      • 2012-04-27
      • 2018-02-27
      • 1970-01-01
      • 1970-01-01
      • 2020-09-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多