如何使Varargs的Mockito验证与实际调用匹配?

fzsnzjdm  于 9个月前  发布在  其他
关注(0)|答案(1)|浏览(77)

我最近从mockito-core版本1.10.19升级到最新版本(5.5.0)。我注意到在验证带有varargs参数的方法调用时,行为发生了变化。
在旧版本中,我使用了一个带有以下签名的自定义匹配器:

private class CustomMatcher<T> extends ArgumentMatcher<T[]> implements VarargMatcher

然而,在最新版本中,由于ArgumentMatcher的变化,我不得不修改签名:

private class CustomMatcher<T> implements ArgumentMatcher<T[]>, Serializable

下面是我的代码的简化版本:

package com.mockito.varargs.test;

import org.mockito.ArgumentMatcher;
import org.mockito.Mockito;

import java.io.Serializable;
import java.lang.reflect.Array;
import java.util.Arrays;

import static org.mockito.ArgumentMatchers.*;

public class TestHelper {

    private final DummyClient client = Mockito.mock(DummyClient.class);

    public TestHelper() {
    }

    public void execute(String... tags) {
        client.record("action", 42L, tags);
        client.histogram("someAspect", 100L, "type:total", "type:cpu", "type:wait", "type:sql");
        new Verifier().verifyTagsAre("tag1", "tag2", "tag3");
    }

    public class Verifier {
        public void verifyTagsAre(final String... expectedTags) {
            Mockito.verify(client).record(eq("action"), eq(42L),
                    argThat(new CustomMatcher<>(expectedTags)));
            Mockito.verify(client, Mockito.times(3)).histogram(eq("someAspect"), anyLong(),
                    argThat(new CustomAnyVarargMatcher<>(
                            new CustomMatcher<>("type:total", expectedTags),
                            new CustomMatcher<>("type:cpu", expectedTags),
                            new CustomMatcher<>("type:wait", expectedTags),
                            new CustomMatcher<>("type:sql", expectedTags)
                    )));
            Mockito.verifyNoMoreInteractions(client);
        }
    }

    private class CustomMatcher<T> implements ArgumentMatcher<T[]>, Serializable {
        private T[] expectedValues;

        CustomMatcher(T... expectedValues) {
            this.expectedValues = expectedValues;
        }

        CustomMatcher(T t, T... expectedValues) {
            T[] values = (T[]) Array.newInstance(t.getClass(), expectedValues.length + 1);
            System.arraycopy(expectedValues, 0, values, 1, expectedValues.length);
            values[0] = t;
            this.expectedValues = values;
        }

        @Override
        public boolean matches(T[] args) {
            return Arrays.equals(expectedValues, args);
        }

        @Override
        public String toString() {
            return Arrays.toString(expectedValues);
        }
    }

    private class CustomAnyVarargMatcher<T> implements ArgumentMatcher<T>, Serializable {
        private ArgumentMatcher<T>[] matchers;

        CustomAnyVarargMatcher(ArgumentMatcher<T>... matchers) {
            this.matchers = matchers;
        }

        @Override
        public boolean matches(T args) {
            for (ArgumentMatcher<T> matcher : matchers) {
                if (matcher.matches(args))
                    return true;
            }
            return false;
        }

        @Override
        public String toString() {
            return Arrays.toString(matchers);
        }
    }

    interface DummyClient {
        void record(String action, long value, String... tags);
        void histogram(String aspect, long value, String... tags);
    }

    public static void main(String[] args) {
        new TestHelper().execute("tag1", "tag2", "tag3");
    }
}

当验证失败时,我得到这个错误:

Exception in thread "main" Argument(s) are different! Wanted:
dummyClient.record(
    "action",
    42L,
    [tag1, tag2, tag3]
);
-> at com.mockito.varargs.test.TestHelper$Verifier.verifyTagsAre(TestHelper.java:26)
Actual invocations have different arguments:
dummyClient.record(
    "action",
    42L,
    "tag1",
    "tag2",
    "tag3"
);

我希望预期的输出与实际的调用匹配,就像在1.10.19版中一样。有没有办法在最新版本中做到这一点?或者有没有一种方法可以在Mockito中将varargs作为数组进行比较,如果数组相等,则验证通过?

ttcibm8c

ttcibm8c1#

你应该能够使用AdditionalMatchers,不需要自定义匹配器:

public void verifyTagsAre(final String... expectedTags) {
    Mockito.verify(client).record(eq("action"), eq(42L),
            AdditionalMatchers.aryEq(expectedTags));
    Mockito.verify(client, Mockito.times(3)).histogram(eq("someAspect"), anyLong(), AdditionalMatchers.aryEq(new String[] { "type:total", "type:cpu", "type:wait", "type:sql" }));
    Mockito.verifyNoMoreInteractions(client);
}

参见GitHub issues mockito#1593mockito#2835,特别是关于argThat的部分:
argThat()行为变化
重大变化:MockitoHamcrest中的所有方法(如argThat())在传递给varargs参数时的行为都发生了变化。在Mockito v5之前,这些匹配器将匹配varargs参数中的每个元素,匹配任何数量的元素0. n。从Mockito v5开始,这些匹配器在传递给varargs参数时,将匹配传递给varargs参数的单个值的调用。要匹配传递给varargs参数的任意数量的值,请将use和varargs数组类型传递给argThat。例如,给定String... varargs参数,用途:argThat(isA(String[].class), String[].class)
而且我希望代码只是问题的一个例子,因为没有真实的代码被测试。您有一个Mockito模拟对象,您在其上调用方法,然后验证该方法已被调用。我甚至不确定这个测试是否有效-histogram方法只被调用了一次,但是验证器检查了times(3)(参数类型实际上并不匹配:expectedTags永远不会传递给histogram)。

相关问题