Sure, here's a patch (I'm hoping the list won't strip it). Notice that
this takes a couple of static fields/methods and makes them test-method
specific. Alternatively, junit 4.10 allows the user to decide between
test-method and test-class level rules. This may be a better way to go,
but the code looks basically the same.

-Jared

On Tue 22 Nov 2011 06:59:12 PM CST, Les Hazlewood wrote:
>
> Hi Jared,
>
> I've not done that before - do you have an example or patch that shows
> how it would work?
>
> Cheers,
>
> Les
>
> On Wed, Nov 23, 2011 at 12:15 AM, Jared Bunting
> <[email protected]> wrote:
>>
>> What about reimplementing it as a Junit rule? There are often other
>> lifecycle bits that I want to incorporate in a unit test and having it
>> implemented as an abstract class seems to make this difficult.
>>
>> On Nov 22, 2011 5:54 PM, "Les Hazlewood" <[email protected]> wrote:
>>
>> While I agree that it is easy to copy-n-paste, I do believe it is
>> easier and more convenient to just depend on a test jar, subclass the
>> test class and be on your way writing tests.
>>
>> Given the two options, my vote is to create a separate 'test' module
>> that people can depend on if they want. People can depend on it if
>> they like, but they can just ignore it if they'd rather copy-n-paste.
>>
>> My .02,
>>
>> Les
>>
>>
>> On Tue, Nov 22, 2011 at 11:45 PM, Allan Ditzel <[email protected]>
>> wrote:
>>>
>>> The main value woul...
>>
>


Index: core/src/test/java/org/apache/shiro/test/ExampleShiroUnitTest.java
===================================================================
--- core/src/test/java/org/apache/shiro/test/ExampleShiroUnitTest.java	(revision 1205266)
+++ core/src/test/java/org/apache/shiro/test/ExampleShiroUnitTest.java	(working copy)
@@ -2,6 +2,7 @@
 
 import org.apache.shiro.subject.Subject;
 import org.junit.After;
+import org.junit.Rule;
 import org.junit.Test;
 
 import static org.easymock.EasyMock.createNiceMock;
@@ -12,8 +13,11 @@
  *
  * @since 1.2
  */
-public class ExampleShiroUnitTest extends AbstractShiroTest {
+public class ExampleShiroUnitTest {
 
+    @Rule
+    public ShiroRule shiro = new ShiroRule();
+
     @Test
     public void testSimple() {
         //1.  Create a mock Subject instance for the test to run
@@ -23,7 +27,7 @@
         expect(subjectUnderTest.isAuthenticated()).andReturn(true);
 
         //2. Bind the subject to the current thread:
-        setSubject(subjectUnderTest);
+        shiro.setSubject(subjectUnderTest);
 
         //perform test logic here.  Any call to
         //SecurityUtils.getSubject() directly (or nested in the
@@ -33,7 +37,7 @@
     @After
     public void tearDownSubject() {
         //3. Unbind the subject from the current thread:
-        clearSubject();
+        shiro.clearSubject();
     }
 
 }
Index: core/src/test/java/org/apache/shiro/test/ShiroRule.java
===================================================================
--- core/src/test/java/org/apache/shiro/test/ShiroRule.java	(revision 0)
+++ core/src/test/java/org/apache/shiro/test/ShiroRule.java	(revision 0)
@@ -0,0 +1,84 @@
+package org.apache.shiro.test;
+
+import org.apache.shiro.SecurityUtils;
+import org.apache.shiro.UnavailableSecurityManagerException;
+import org.apache.shiro.mgt.SecurityManager;
+import org.apache.shiro.subject.Subject;
+import org.apache.shiro.subject.support.SubjectThreadState;
+import org.apache.shiro.util.LifecycleUtils;
+import org.apache.shiro.util.ThreadState;
+import org.junit.AfterClass;
+import org.junit.rules.MethodRule;
+import org.junit.runners.model.FrameworkMethod;
+import org.junit.runners.model.Statement;
+
+public class ShiroRule implements MethodRule {
+    private ThreadState subjectThreadState;
+
+    /**
+     * Allows subclasses to set the currently executing {@link org.apache.shiro.subject.Subject} instance.
+     *
+     * @param subject the Subject instance
+     */
+    protected void setSubject(Subject subject) {
+        clearSubject();
+        subjectThreadState = createThreadState(subject);
+        subjectThreadState.bind();
+    }
+
+    protected Subject getSubject() {
+        return SecurityUtils.getSubject();
+    }
+
+    protected ThreadState createThreadState(Subject subject) {
+        return new SubjectThreadState(subject);
+    }
+
+    /**
+     * Clears Shiro's thread state, ensuring the thread remains clean for future test execution.
+     */
+    protected void clearSubject() {
+        doClearSubject();
+    }
+
+    private void doClearSubject() {
+        if (subjectThreadState != null) {
+            subjectThreadState.clear();
+            subjectThreadState = null;
+        }
+    }
+
+    public void setSecurityManager(org.apache.shiro.mgt.SecurityManager securityManager) {
+        SecurityUtils.setSecurityManager(securityManager);
+    }
+
+    public SecurityManager getSecurityManager() {
+        return SecurityUtils.getSecurityManager();
+    }
+
+    public Statement apply(final Statement base, FrameworkMethod method, Object target) {
+        return new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+                try {
+                    base.evaluate();
+                } finally {
+                    ShiroRule.this.tearDownShiro();
+                }
+            }
+        };
+    }
+
+    private void tearDownShiro() {
+        doClearSubject();
+        try {
+            SecurityManager securityManager = getSecurityManager();
+            LifecycleUtils.destroy(securityManager);
+        } catch (UnavailableSecurityManagerException e) {
+            //we don't care about this when cleaning up the test environment
+            //(for example, maybe the subclass is a unit test and it didn't
+            // need a SecurityManager instance because it was using only mock Subject instances)
+        }
+        setSecurityManager(null);
+    }
+}

Reply via email to