I'm using EasyMock (3.2). I want to write a test for part of my security system based on Spring Security. I want to mock the Authentication
so that it returns empty list of authorities. Its method declaration is as follows:
Collection<? extends GrantedAuthority> getAuthorities();
So I write a test:
Authentication authentication = createMock(Authentication.class);
Collection<? extends GrantedAuthority> authorities = Collections.emptyList();
expect(authentication.getAuthorities()).andReturn(authorities);
But the compiler is complaining about the third line on andReturn
call:
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>
What am I doing wrong?
UPDATE:
When I change the declaration of authorities
to:
Collection<GrantedAuthority> authorities = Collections.emptyList();
as suggested, it still does not compile, but the error is a bit different:
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>)
I ensured that the GrantedAuthority
is actually the same in both declarations - org.springframework.security.core.GrantedAuthority
.
Remove the item type from the collection declaration, you'll get a warning but the test will work.
@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);
}
There is no easy way you can test this function using any mocking framework. I am afraid you may have to resort to type casting. The reason being generics are skin deep when it comes to wildcards - ie you can specify wildcards only at the top level. So, EasyMock or any mocking framework, cannot effectively create a mock object of something which has a wildcard in it. If you look at the message:
IExpectationSetters<Collection<? extends GrantedAuthority>>
is two level deep and that is why compiler gives it up.
The easiest is to resort to expectLastCall()
instead:
authentication.getAuthorities();
expectLastCall().andReturn(authorities);
Let's examine both methods:
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;
}
So both methods do basically the same thing but expect(T value)
forces IExpectationSetters
to be of the same type as value
while expectLastCall()
pretty much returns everything you want.
Collection<? extends GrantedAuthority>
Collection<? extends GrantedAuthority>
means a Collection
of some unknown class that extends GrantedAuthority
. So, two separate declarations of Collection<? extends GrantedAuthority>
Collection<? extends GrantedAuthority>
could potentially be two different classes in regards to the wildcard. That's why the compiler complains.
You may just want Collection<GrantedAuthority>
, which means a Collection
of GrantedAuthority
, which of course would permit any instance of GrantedAuthority
or any of its subclasses to be added to the collection.
A solution to this specific problem is to mock the AbstractAuthenticationToken
class instead of the Authentication
interface. The default implementation from spring overrides the getPrincipals()
method and changes the returned type from Collection<? extends GrantedAuthority>
Collection<? extends GrantedAuthority>
to Collection<GrantedAuthority>
. Working code:
AbstractAuthenticationToken authentication = createMock(AbstractAuthenticationToken.class);
Collection<GrantedAuthority> authorities = Collections.emptyList();
expect(authentication.getAuthorities()).andReturn(authorities);
However, this does not solve the problem in general.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.