@Override
    public Class<T> getInterface() {
        return directory.getInterface();
    }
@Test
    void testGetMenuSuccessfully() {

        // setup
        url = url.addParameter(MERGER_KEY, ".merge");

        given(invocation.getMethodName()).willReturn("getMenu");
        given(invocation.getParameterTypes()).willReturn(new Class<?>[] {});
        given(invocation.getArguments()).willReturn(new Object[] {});
        given(invocation.getObjectAttachments()).willReturn(new HashMap<>());
        given(invocation.getInvoker()).willReturn(firstInvoker);

        firstInvoker = (Invoker) Proxy.newProxyInstance(
                getClass().getClassLoader(), new Class<?>[] {Invoker.class}, (proxy, method, args) -> {
                    if ("getUrl".equals(method.getName())) {
                        return url.addParameter(GROUP_KEY, "first");
                    }
                    if ("getInterface".equals(method.getName())) {
                        return MenuService.class;
                    }
                    if ("invoke".equals(method.getName())) {
                        return AsyncRpcResult.newDefaultAsyncResult(firstMenu, invocation);
                    }
                    return null;
                });

        secondInvoker = (Invoker) Proxy.newProxyInstance(
                getClass().getClassLoader(), new Class<?>[] {Invoker.class}, (proxy, method, args) -> {
                    if ("getUrl".equals(method.getName())) {
                        return url.addParameter(GROUP_KEY, "second");
                    }
                    if ("getInterface".equals(method.getName())) {
                        return MenuService.class;
                    }
                    if ("invoke".equals(method.getName())) {
                        return AsyncRpcResult.newDefaultAsyncResult(secondMenu, invocation);
                    }
                    return null;
                });

        given(directory.list(invocation)).willReturn(new ArrayList() {

            {
                add(firstInvoker);
                add(secondInvoker);
            }
        });
        given(directory.getUrl()).willReturn(url);
        given(directory.getConsumerUrl()).willReturn(url);
        given(directory.getConsumerUrl()).willReturn(url);
        given(directory.getInterface()).willReturn(MenuService.class);

        mergeableClusterInvoker = new MergeableClusterInvoker<MenuService>(directory);

        // invoke
        Result result = mergeableClusterInvoker.invoke(invocation);
        assertTrue(result.getValue() instanceof Menu);
        Menu menu = (Menu) result.getValue();
        Map<String, List<String>> expected = new HashMap<>();
        merge(expected, firstMenuMap);
        merge(expected, secondMenuMap);
        assertEquals(expected.keySet(), menu.getMenus().keySet());
        for (Map.Entry<String, List<String>> entry : expected.entrySet()) {
            // FIXME: cannot guarantee the sequence of the merge result, check implementation in
            // MergeableClusterInvoker#invoke
            List<String> values1 = new ArrayList<>(entry.getValue());
            List<String> values2 = new ArrayList<>(menu.getMenus().get(entry.getKey()));
            Collections.sort(values1);
            Collections.sort(values2);
            assertEquals(values1, values2);
        }
    }