如何使用通配符模拟泛型方法的行为

时间:2013-11-26 14:42:08

标签: java generics easymock

我正在使用EasyMock(3.2)。我想基于Spring Security为我的部分安全系统编写一个测试。我想模仿Authentication,以便它返回空的权限列表。其方法声明如下:

 Collection<? extends GrantedAuthority> getAuthorities();

所以我写了一个测试:

Authentication authentication = createMock(Authentication.class);
Collection<? extends GrantedAuthority> authorities = Collections.emptyList();
expect(authentication.getAuthorities()).andReturn(authorities);

但是编译器抱怨andReturn调用的第三行:

The method andReturn(Collection<capture#1-of ? extends GrantedAuthority>) in the type IExpectationSetters<Collection<capture#1-of ? extends GrantedAuthority>> is not applicable for the arguments (Collection<capture#2-of ? extends GrantedAuthority>

我做错了什么?


更新:

当我将authorities的声明更改为:

Collection<GrantedAuthority> authorities = Collections.emptyList();

按照建议,它仍然无法编译,但错误有点不同:

The method andReturn(Collection<capture#1-of ? extends GrantedAuthority>) in the type IExpectationSetters<Collection<capture#1-of ? extends GrantedAuthority>> is not applicable for the arguments (Collection<GrantedAuthority>)

我确保GrantedAuthority在两个声明中实际上都是相同的 - org.springframework.security.core.GrantedAuthority

5 个答案:

答案 0 :(得分:6)

从集合声明中删除项目类型,您将收到警告但测试仍然有效。

@Test
public void testFoo()
    {
    // setup
    Authentication mockAuthentication = createMock(Authentication.class);
    Collection authorities = Collections.emptyList();
    expect(mockAuthentication.getAuthorities()).andReturn(authorities);

    // exercise
    EasyMock.replay(mockAuthentication);
    Collection<? extends GrantedAuthority> retAuth = mockAuthentication.getAuthorities();

    // verify
    EasyMock.verify(mockAuthentication);
    assertEquals(authorities, retAuth);
    }

答案 1 :(得分:2)

没有简单的方法可以使用任何模拟框架测试此功能。恐怕你可能不得不求助于铸造。对于通配符而言,泛型的原因是皮肤深层 - 即您只能在顶层指定通配符。因此,EasyMock或任何模拟框架都无法有效地创建一个带有通配符的模拟对象。 如果你看一下这条消息:

 IExpectationSetters<Collection<? extends GrantedAuthority>>

是两级深度,这就是编译器放弃它的原因。

答案 2 :(得分:1)

最简单的方法是改为使用expectLastCall()

authentication.getAuthorities();
expectLastCall().andReturn(authorities);

让我们检查两种方法:

public static <T> IExpectationSetters<T> expect(final T value) {
    return getControlForLastCall();
}

public static <T> IExpectationSetters<T> expectLastCall() {
    return getControlForLastCall();
}

@SuppressWarnings("unchecked")
private static <T> IExpectationSetters<T> getControlForLastCall() {
    // snip
    return (IExpectationSetters<T>) lastControl;
}

因此,两种方法的基本相同,但expect(T value)强制IExpectationSettersvalue的类型相同,而expectLastCall()几乎可以返回您想要的所有内容。

答案 3 :(得分:0)

Collection<? extends GrantedAuthority>表示扩展Collection某些未知类GrantedAuthority。因此,对于通配符,Collection<? extends GrantedAuthority>的两个单独声明可能是两个不同的类。这就是编译器抱怨的原因。

您可能只需要Collection<GrantedAuthority>,这意味着Collection GrantedAuthority,这当然会允许添加GrantedAuthority或其任何子类的任何实例集合。

答案 4 :(得分:0)

此特定问题的解决方案是模拟AbstractAuthenticationToken类而不是Authentication接口。 spring中的默认实现会覆盖getPrincipals()方法,并将返回的类型从Collection<? extends GrantedAuthority>更改为Collection<GrantedAuthority>。工作代码:

AbstractAuthenticationToken authentication = createMock(AbstractAuthenticationToken.class);
Collection<GrantedAuthority> authorities = Collections.emptyList();
expect(authentication.getAuthorities()).andReturn(authorities);

然而,这并不能解决一般问题。