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);
+ }
+}