Hi Tauren,

Matt Brictson recently posted an even better way using mock objects.
You can create a mock Subject instance (since Subject is an interface,
this works great with frameworks like Easymock and Mockito) and mock
what would happen during a security check.  Then you bind and unbind
your mock to the thread as necessary.

If you're using the AbstractShiroTest base class as shown in the
testing.html documentation page, this is all you would have to do (for
example, using JUnit 4 annotations and Easymock):

import static org.easymock.EasyMock.*;

public class MyTest extends AbstractShiroTest {

    @Test
    public void testSomething() {
        Subject subjectUnderTest = createNiceMock(Subject.class);
        expect(subjectUnderTest.isAuthenticated()).andReturn(true);

        //bind the Subject to the thread so SecurityUtils and
        //other framework code will work:
        setSubject(subjectUnderTest);

        //execute your test logic
    }

}

Note that your mock subject will remain bound to the thread for all
tests in a class unless you call the clearSubject() method after each
test.

This can be done in a JUnit @After method for example:

@After
public void tearDownSubject() {
    clearSubject();
}

Or, some people may want that to be the case for all tests.  You can
mix and match this setup/teardown logic in each method manually or use
the @Before and @After annotations as you see fit.  The
AbstractShiroTest class will however unbind the Subject from the
thread at the end of the test class execution because of the
@AfterClass annotation in its tearDownShiro() method.

The reason this is probably a better approach for most tests is that
most people don't really care about testing the Shiro SecurityManager
and Subject implementations - they typically want to test their own
code that relies on SecurityUtils.getSubject() and the Subject API.
Creating and using the actual SecurityManager and Subject
implementation instances (as is shown in the current testing.html
documentation) is probably better suited for integration tests.

I'll update the documentation to reflect this approach.

HTH!

Best,

Les

On Tue, Dec 7, 2010 at 7:45 AM, Tauren Mills <[email protected]> wrote:
> I'm following the instructions on how to do unit testing with Shiro:
> http://shiro.apache.org/testing.html
> I've got everything compiling and running. However, I'm unclear on how to
> actually specify the subject I want to use. The following creates a
> DelegatingSubject, but it appears to be unauthenticated with no principles
> or anything. I know I haven't properly set them, but I'm unsure the right
> way to do it.
>         Subject subjectUnderTest = new
> Subject.Builder(getSecurityManager()).buildSubject();
> My application uses Spring to configure Shiro and it has a custom realm that
> creates permissions. Do I need to create another realm to use only during
> testing? Or can I use my curent realm and force a specific user
> authentication?  For instance, I'd like to run my tests assuming that the
> user with an ID of 1 has authenticated.
> Following the testing instructions, I've set up an INI file that gets
> loaded, even though with my normal application, I use Spring to configure
> Shiro. It seems like going with an INI for testing might be simpler. Should
> I be using Spring to configure Shiro for unit testing, or is using INI just
> fine?
> [main]
> sha256Matcher = org.apache.shiro.authc.credential.Sha256CredentialsMatcher
> rememberMeManager = com.company.security.MyRememberMeManager
> rememberMeManager.cipherKey = xymVvsqSTov2/tcoHnax0B==
> myRealm = com.company.security.MyRealm
> myRealm.credentialsMatcher = $sha256Matcher
> securityManager.sessionManager.globalSessionTimeout = 1800000
> securityManager.rememberMeManager = $rememberMeManager
> [users]
> [roles]
> So what is the simplest way to make user ID 1 authenticated? Should I create
> a custom Realm for testing that has the user hard coded into
> doGetAuthenticationInfo? Or should I be specify the user in the [users]
> section and somehow use it? Or can I pass a custom AuthenticationToken with
> the proper details to my current Realm implementation? How would I do that?
>     protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken
> authcToken) throws AuthenticationException {
>         UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
>         Member member = memberService.findMember(token.getUsername());
>         if (member != null && member.isValidated()) {
>             return new SimpleAuthenticationInfo(member.getId(),
> member.getPassword(), getName());
>         } else {
>             return null;
>         }
>     }
> Ideally, I'd just build an AuthenticationToken and somehow pass it to the
> realm.doGetAuthInfo() method. But I'm unclear how that would be
> accomplished.
> Thanks!
> Tauren

Reply via email to