天天看點

shiro實戰系列(十三)之單元測試

由于我們已經涉及到了 Subject reference,我們知道 Subject 是“目前執行”使用者的特定安全視圖,且該 Subject 實 例綁定到一個線程來確定我們知道線上程執行期間的任何時間是誰在執行邏輯。   這意味着三個基本的東西必須始終出現,為了能夠支援通路目前正在執行的 Subject:

1. 必須建立一個 Subject 執行個體

2. Subject 執行個體必須綁定目前執行的線程。

3. 線上程完成執行後(或如果該線程執行抛出異常),該 Subject 必須解除綁定來確定該線程在任何線程池環境 中保持'clean'。

Shiro 擁有為正在運作的應用程式自動地執行綁定//解除綁定邏輯的建築元件。例如,在 Web 應用程式中,當過濾 一個請求時,Shiro 的根過濾器執行該邏輯。但由于測試環境和架構不同,我們需要自己選擇自己的測試架構來執 行此綁定/解除綁定邏輯。

Test Setup

我們知道在建立一個 Subject 執行個體後,它必須被綁定線程。在該線程(或在這個例子中,是一個 test)完成執行後, 我們必須解除 Subject 的綁定來保持線程的'clean'.   幸運的是,現代測試架構如 JUnit 和 TestNG 已經能夠在本地支援'setup'和'teardown'的概念。我們可以利用這一支援 來模拟 Shiro 在一個“完整的”應用程式中會做些什麼。我們已經在下面建立了一個你能夠在你自己的測試中使用 的抽象基類——随意複制和修改如果你覺得合适的話。它能夠在單元測試和內建測試中使用(我在本例中使用 JUnit, 但 TestNG 也能夠工作得很好):     

shiro實戰系列(十三)之單元測試
shiro實戰系列(十三)之單元測試
shiro實戰系列(十三)之單元測試

Testing & Frameworks                

在 AbstractShiroTest 類中的代碼使用 Shiro 的 ThreadState 概念及一個靜态的 SecurityManager。這些技術在測試和框 架代碼中是很有用的,但幾乎不曾在應用程式代碼中使用。 大多數使用 Shiro 工作的需要確定線程的一緻性的終端使用者,幾乎總是使用 Shiro 的自動管理機制,即 Subject.associateWith 和 Subject.execute 方法。這些方法包含在 Subject thread association 參考文獻中。

Unit Testing

單元測試主要是測試你的代碼,且你的代碼是在有限的作用域内。當你考慮到 Shiro 時,你真正要關注的是你的代 碼能夠與 Shiro 的 API 正确的運作——你不會想做關于 Shiro 的實作是否工作正常(這是 Shiro 開發團隊在 Shiro 的代 碼庫必須確定的東西)的必要測試。   測試 Shiro 的實作是否與你的實作協同工作是真實的內建測試(下面讨論)。

ExampleShiroUnitTest

由于單元測試适用于測試你的邏輯(而不是你可能調用的任何實作),這對于模拟你邏輯所依賴的任何 API 來說是 個很好的主意。這能夠與 Shiro 工作得很好——你可以模拟 Subject 執行個體,并使它反映任何情況下你所需的反應,這 些反應是處于測試的代碼做出的。         但正如上文所述,在 Shiro 測試中關鍵是要記住在測試執行期間任何 Subject 執行個體(模拟的或真實的)必須綁定到線 程。是以,我們所需要做的是綁定模拟的 Subject 以確定如預期進行。   (這個例子使用 EasyMock,但 Mockito 也同樣地工作得很好):

shiro實戰系列(十三)之單元測試

正如你所看到的,我們沒有設立一個 Shiro SecurityManager 執行個體或配置一個 Realm 或任何像這樣的東西。我們簡單 地建立一個模拟 Subject 執行個體,并通過調用 setSubject 方法将它綁定到線程。這将確定任何在我們測試代碼中的調用 或在代碼中我們正測試的 SecurityUtils.getSubject()正常工作。   請注意,setSubject 方法實作将綁定你的模拟 Subject 到線程,且它仍将存在,直到你通過一個不同的 Subject 調用 setSubject 或直到你明确地通過調用 clearSubject()将它從線程中清除。   保持 Subject 綁定到該線程多長時間(或在一個不同的測試中用來交換一個新的執行個體)取決于你及你的測試需求。

tearDownSubject()

 在執行個體中的 tearDownSubject()方法使用了 Junit 4 的注釋來確定該 Subject 在每個測試方法執行後被清除,不管發生 什麼。這要求你設立一個新的 Subject 執行個體并将它設定到每個需要執行的測試中。   然而這也不是絕對必要的。例如,你可以隻每個測試開始時綁定一個新的 Subject 執行個體(通過 setSubject),也就是 說,使用@Before-annotated 方法。但如果你将要這麼做,你可以同時使用@After tearDownSubject() 方法來保持對 稱及'clean'。   你可以手動地在每個方法中混合及比對該 setup/teardown 邏輯或使用@Before 和@After 注釋隻要你認為合适。所有 測試完成後,AbstractShiroTest 超類在無論怎樣都會将 Subject 從線程解除綁定,因為@After 注釋在它的 tearDownShiro()方法中。  

Integration Testing

現在我們讨論了單元測試的設定,讓我們讨論一些關于內建測試的東西。內建測試是指測試跨 API 邊界的實作。例 如,測試當調用 B 實作時 A 實作是否工作,且 B 實作是否做它該做的事情。   你同樣也可以在 Shiro 中輕松地執行內建測試。Shiro 的 SecurityManager 執行個體及它所包含的東西(如 Realms 和 SessionManager 等)都是占用很少記憶體的非常輕量級的 POJO。這意味着你可以為每一個你執行的測試類建立并銷毀 一個 SecurityManager 執行個體。當你的內建測試運作時,它們将使用“真實的”SecurityManager,且與你應用程式中 相像的 Subject 執行個體将會在運作時使用。 

 ExampleShiroIntegrationTest 下面的執行個體代碼看起來與上面的單元測試執行個體幾乎相同,但這 3 個步驟卻有些不同:

1. 現在有了 step '0',它用來設立一個“真實的”SecurityManager 執行個體。

2. Step 1 現在通過 Subject.Builder 構造一個“真實的”Subject 執行個體,并将它綁定到線程。

線程的綁定與解除綁定(step 2 和 3)與單元測試執行個體中的作用一樣。

shiro實戰系列(十三)之單元測試
shiro實戰系列(十三)之單元測試

正如你所看到的,一個具體的 SecurityManager 實作被執行個體化,并通過 setSecurityManager 方法使其餘的測試能夠對 其進行通路。然後測試方法能夠使用該 SecurityManager,當使用 Subject.Builder 後通過調用 getSecurityManager()方 法。   還要注意 SecurityManager 執行個體在@BeforeClass 設定方法中隻被設定一次——一個對于大多數測試類較為普遍的做 法。如果你想,你可以建立一個新的 SecurityManager 執行個體并在任何時候從任何測試方法通過 setSerurityManager 來 設定它——例如,你可能會引用兩個不同的.ini 檔案來建構一個根據你的測試需求而來的新 SecurityManager。   最後,與單元測試例子一樣,AbstractShiroTest 超類将會清除所有 Shiro 産物(任何存在的 SecurityManager 及 Subject 執行個體)通過它的@AfterClass tearDownShiro()方法來確定該線程在下個測試類運作時是'clean'的。