当前位置: 首页 > 面试题库 >

Mockito; 用列表调用verify方法,忽略列表中元素的顺序

郑胡媚
2023-03-14
问题内容

我有一个类(ClassA),可以在目录中获取文件。它在给定目录中扫描与正则表达式匹配的文件。对于每个匹配的文件,它将文件对象添加到列表中。处理目录后,它将文件列表传递给另一个类(ClassB)进行处理

我正在编写ClassA的单元测试,因此正在使用Mockito模拟ClassB,并将其注入ClassA。然后,我想在不同情况下验证传递给ClassB的列表的内容(即我的模拟)

我将代码剥离到以下代码

public class ClassA implements Runnable {

    private final ClassB classB;

    public ClassA(final ClassB classB) {
        this.classB = classB;
    }

    public List<File> getFilesFromDirectories() {
        final List<File> newFileList = new ArrayList<File>();
        //        ...
        return newFileList;
    }

    public void run() {
        final List<File> fileList = getFilesFromDirectories();

        if (fileList.isEmpty()) {
            //Log Message
        } else {
            classB.sendEvent(fileList);
        }
    }
}

测试类看起来像这样

    @RunWith(MockitoJUnitRunner.class)
    public class AppTest {

    @Rule
    public TemporaryFolder folder = new TemporaryFolder();

    @Mock
    private ClassB mockClassB;

    private File testFileOne;

    private File testFileTwo;

    private File testFileThree;

    @Before
    public void setup() throws IOException {
        testFileOne = folder.newFile("testFileA.txt");
        testFileTwo = folder.newFile("testFileB.txt");
        testFileThree = folder.newFile("testFileC.txt");
    }

    @Test
    public void run_secondFileCollectorRun_shouldNotProcessSameFilesAgainBecauseofDotLastFile() throws Exception {
        final ClassA objUndertest = new ClassA(mockClassB);

        final List<File> expectedFileList = createSortedExpectedFileList(testFileOne, testFileTwo, testFileThree);
        objUndertest.run();

        verify(mockClassB).sendEvent(expectedFileList);
    }

    private List<File> createSortedExpectedFileList(final File... files) {
        final List<File> expectedFileList = new ArrayList<File>();
        for (final File file : files) {
            expectedFileList.add(file);
        }
        Collections.sort(expectedFileList);
        return expectedFileList;
    }
}

问题是该测试在Windows上可以正常运行,但在Linux上无法通过。原因是在Windows上,ClassA列出文件的顺序与ExpectedList匹配,所以该行

verify(mockClassB).sendEvent(expectedFileList);

在Windows上导致问题expecetdFileList =
{FileA,FileB,FileC},而在Linux上将是{FileC,FileB,FileA},因此验证失败。

问题是,我该如何在Mockito中解决这个问题。有什么说法,我希望可以使用此参数来调用此方法,但是我不在乎列表内容的顺序。

我确实有一个解决方案,但我不喜欢它,而是希望有一个更简洁,更易于阅读的解决方案。

我可以使用ArgumentCaptor获取传递给模拟的实际值,然后可以对其进行排序,并将其与我的期望值进行比较。

    final ArgumentCaptor<List> argument = ArgumentCaptor.forClass(List.class);
    verify(mockClassB).method(argument.capture());
    Collections.sort(expected);
    final List<String> value = argument.getValue();
    Collections.sort(value);
    assertEquals(expecetdFileList, value);

问题答案:

如另一个答案中所述,如果您不关心顺序,则可能最好更改界面,使其不关心顺序。

如果顺序在代码中很重要,但在特定测试中不重要,则可以像平常一样使用ArgumentCaptor。它会使代码有些混乱。

如果这是您可以在多个测试中执行的操作,则最好使用适当的Mockito
Matchers
或Hamcrest

Matchers,或者自己动手制作(如果找不到满足您需求的产品)。hamcrest匹配器可能是最好的,因为除了mockito之外,它还可以在其他环境中使用。

对于此示例,您可以创建一个hamcrest匹配器,如下所示:

import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.hamcrest.Matcher;

import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class MyMatchers {
    public  static <T> Matcher<List<T>> sameAsSet(final List<T> expectedList) {
        return new BaseMatcher<List<T>>(){
            @Override
            public boolean matches(Object o) {
                List<T> actualList = Collections.EMPTY_LIST;
                try {
                    actualList = (List<T>) o;
                }
                catch (ClassCastException e) {
                    return false;
                }
                Set<T> expectedSet = new HashSet<T>(expectedList);
                Set<T> actualSet = new HashSet<T>(actualList);
                return actualSet.equals(expectedSet);
            }

            @Override
            public void describeTo(Description description) {
                description.appendText("should contain all and only elements of ").appendValue(expectedList);
            }
        };
    }
}

然后,验证代码变为:

verify(mockClassB).sendEvent(argThat(MyMatchers.sameAsSet(expectedFileList)));

如果您改为创建一个模仿匹配器,则不需要argThat,它基本上将hamcrest匹配器包装在模仿匹配器中。

这将排序或转换的逻辑移到测试之外,并使其可重用。



 类似资料:
  • 问题内容: 我认为那应该是一个非常简单的问题。但是不知何故我无法在Google中找到答案。 假设我有2个字符串列表。第一个包含 “字符串A”和“字符串B” ,第二个包含 “字符串B”和“字符串A” (注意顺序不同)。我想用 JUnit 测试它们,以检查它们是否包含 完全相同的 字符串。 是否有任何断言可以检查忽略顺序的字符串是否相等?对于给定的示例org.junit.Assert.assertEq

  • 忽略列表显示不需要处理的资源或一类规则建议。 忽略列表显示不需要处理的资源或一类规则建议。当某资源或某类型的建议不需要用户处理时,可以通过忽略或忽略该类建议功能将其显示在忽略列表。 入口:在云管平台单击左上角导航菜单,在弹出的左侧菜单栏中单击 “优化建议/安全检查/忽略列表” 菜单项,进入忽略列表页面。 恢复 当忽略列表中某资源或某规则建议需要用户关注处理时,可以使用恢复功能将其显示在建议列表。

  • 忽略列表显示不需要处理的资源或一类规则建议。 忽略列表显示不需要处理的资源或一类规则建议。当某资源或某类型的建议不需要用户处理时,可以通过忽略或忽略该类建议功能将其显示在忽略列表。 入口:在云管平台单击左上角导航菜单,在弹出的左侧菜单栏中单击 “优化建议/成本优化/忽略列表” 菜单项,进入忽略列表页面。 恢复 当忽略列表中某资源或某规则建议需要用户关注处理时,可以使用恢复功能将其显示在建议列表。

  • 现在,如果我使用以下代码进行比较,我将在XMLUnit的帮助下相互比较两个xml文件 现在它给出了:预期的属性值'02',但是'01',但我不希望有差异,我希望表id是唯一的,如果在另一个文件中看到相同的表id,则只检查本例中的Main-Element:table->包含什么。

  • 问题内容: 假设我有汽车清单: 如何缩短上面的代码?简而言之,如何在List的每个元素上调用方法? 例如,在Python中: 问题答案: 更新: 有关使用lambda表达式的Java8解决方案,请参见aaiezza的答案。 Java 8之前的原始答案: 使用Guava可以实现效果,实现比您现有的更加冗长: (请记住,返回的视图将延迟应用该函数- 如果要立即复制,则需要将返回的列表复制到新列表中。)

  • 我正在尝试比较自定义对象的列表。我设置了LEVENSHTEIN_DISTANCE并创建了自定义比较器。对象之间唯一的区别是列表中值的顺序。我希望“没有变化”,但我得到了listchange。结果和示例如下。我做错了什么? 非常感谢和问候,安德烈