Shiro User Manual-Testing

1. Test Setup
创建的Subject实例,必须要绑定到当前线程上,当执行完毕,需要解除绑定。现在流行的测试框架像JUnit,TestNG都支持"setup"和"teardown"。我们可以利用这个特性模拟Shiro在应用中的完整操作。我们创建了AbstractShiroTest抽象类,可用于单元测试和集成测试:

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;

/**
* Abstract test case enabling Shiro in test environments.
*/
public abstract class AbstractShiroTest {

private static ThreadState subjectThreadState;

public AbstractShiroTest() {
}

/**
* Allows subclasses to set the currently executing {@link 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 static void doClearSubject() {
if (subjectThreadState != null) {
subjectThreadState.clear();
subjectThreadState = null;
}
}

protected static void setSecurityManager(SecurityManager securityManager) {
SecurityUtils.setSecurityManager(securityManager);
}

protected static SecurityManager getSecurityManager() {
return SecurityUtils.getSecurityManager();
}

@AfterClass
public static 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);
}
}


2. Unit Testing
由于单元测试测试的是业务逻辑,那么就可以使用EasyMock和Mockito对逻辑依赖的Shiro API进行mock。Shiro对此有很好的支持-可以模拟Subject实例,只需要注意,Subject实例要与当前线程绑定。下面这个例子使用了EasyMock:

import org.apache.shiro.subject.Subject;
import org.junit.After;
import org.junit.Test;

import static org.easymock.EasyMock.*;

/**
* Simple example test class showing how one may perform unit tests for code that requires Shiro APIs.
*/
public class ExampleShiroUnitTest extends AbstractShiroTest {

@Test
public void testSimple() {

//1. Create a mock authenticated Subject instance for the test to run:
Subject subjectUnderTest = createNiceMock(Subject.class);
expect(subjectUnderTest.isAuthenticated()).andReturn(true);

//2. Bind the subject to the current thread:
setSubject(subjectUnderTest);

//perform test logic here. Any call to
//SecurityUtils.getSubject() directly (or nested in the
//call stack) will work properly.
}

@After
public void tearDownSubject() {
//3. Unbind the subject from the current thread:
clearSubject();
}

}

可以看出,我们没有初始化SecurityManager和Realm实例,只是通过setSubject方法将模拟的Subject实例绑定到当前线程上。这就保证了测试时,没有任何问题。需要注意,setSubject将模拟的Subject对象绑定到当前线程,当再次以不同的Subject参数调用setSubject方法或执行clearSubject()方法时,才解除Subject与线程的绑定。

3. Integration Testing
也可以很容易地在Shiro里做集成测试。SecurityManager实例和其封装的组件,都是轻量级的POJO,占用很少的内存。这意味着,可以在每次测试时,创建和销毁SecurityManager实例。执行集成测试时,可以使用真实的SecurityManager和Subject实例:

import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.junit.After;
import org.junit.BeforeClass;
import org.junit.Test;

public class ExampleShiroIntegrationTest extends AbstractShiroTest {

@BeforeClass
public static void beforeClass() {
//0. Build and set the SecurityManager used to build Subject instances used in your tests
// This typically only needs to be done once per class if your shiro.ini doesn't change,
// otherwise, you'll need to do this logic in each test that is different
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:test.shiro.ini");
setSecurityManager(factory.getInstance());
}

@Test
public void testSimple() {
//1. Build the Subject instance for the test to run:
Subject subjectUnderTest = new Subject.Builder(getSecurityManager()).buildSubject();

//2. Bind the subject to the current thread:
setSubject(subjectUnderTest);

//perform test logic here. Any call to
//SecurityUtils.getSubject() directly (or nested in the
//call stack) will work properly.
}

@AfterClass
public void tearDownSubject() {
//3. Unbind the subject from the current thread:
clearSubject();
}
}

可以看出,集成测试和单元测试有不一样的地方:
1. 通过@BeforeClass创建了真实的SecurityManager实例。
2. @Test方法里也创建了真实的Subject,并绑定到线程里。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值