的Mockito;使用list调用verify方法,忽略列表中元素的顺序 [英] Mockito; verify method was called with list, ignore order of elements in list

查看:153
本文介绍了的Mockito;使用list调用verify方法,忽略列表中元素的顺序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a class (ClassA) that get the files in a directory. It scans the given directory for files matching a regex. For each matching file, it adds a File Object to a list. Once the directory is processed, it passes the List of Files to another Class (ClassB) for processing

我正在为ClassA编写单元测试,所以我在嘲笑ClassB使用Mockito,并将其注入ClassA。
然后我想在不同的场景中验证传递给ClassB的列表的内容(即我的模拟)

I am writing unit tests for ClassA, so am mocking ClassB using Mockito, and injecting it into ClassA. I then want to verify in different scenarios the contents of the list that is passed to ClassB (ie my mock)

我已经将代码剥离了以下

I've stripped back the code to the following

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匹配,因此行

The problem is that this test works perfectly fine on windows, but fails on Linux. The reason being that on windows, the order that ClassA list the files matches the expectedList, so the line

verify(mockClassB).sendEvent(expectedFileList);

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

is causing the problem expecetdFileList = {FileA, FileB, FileC} on Windows, whereas on Linux it will be {FileC, FileB, FileA}, so the verify fails.

问题是,我如何在Mockito中解决这个问题。有没有办法说,我希望用这个参数调用这个方法,但我不关心列表内容的顺序。

The question is, how do I get around this in Mockito. Is there any way of saying, I expect this method to be be called with this parameter, but I don't care about the order of the contents of the list.

我确实有一个解决方案,我只是不喜欢它,我宁愿有一个更清晰,更容易阅读的解决方案。

I do have a solution, I just don't like it, I would rather have a cleaner, easier to read solution.

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

I can use an ArgumentCaptor to get the actual value passed into the mock, then can sort it, and compare it to my expected values.

    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);


推荐答案

如另一个答案所述,如果你不这样做关心订单,您可能最好更改界面,因此它不关心订单。

As noted in another answer, if you don't care about the order, you might do best to change the interface so it doesn't care about the order.

如果订单在代码中有问题,但在特定测试中不重要,你可以像你一样使用 ArgumentCaptor 。它使代码变得混乱。

If order matters in the code but not in a specific test, you can use the ArgumentCaptor as you did. It clutters the code a bit.

如果这是你可能在多次测试中做的事情,你可能最好使用适当的 Mockito Matchers Hamcrest Matchers ,或自己动手(如果找不到满足需求的话)。 hamcrest匹配器可能是最好的,因为它可以在mockito之外的其他环境中使用。

If this is something you might do in multiple tests, you might do better to use appropriate Mockito Matchers or Hamcrest Matchers, or roll your own (if you don't find one that fills the need). A hamcrest matcher might be best as it can be used in other contexts besides mockito.

对于这个例子,你可以创建一个hamcrest匹配器,如下所示:

For this example you could create a hamcrest matcher as follows:

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)));

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

If you instead created a mockito matcher, you wouldn't need the argThat, which basically wraps a hamcrest matcher in a mockito matcher.

这会将排序或转换的逻辑移动到你的测试和制作之外它可以重复使用。

This moves the logic of sorting or converting to set out of your test and makes it reusable.

这篇关于的Mockito;使用list调用verify方法,忽略列表中元素的顺序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆