先行一步
VMware 提供培訓和認證,助你加速進步。
瞭解更多在本文中,我們將繼續討論如何在“單頁應用”中使用Spring Security與Angular JS。在這裡,我們將展示如何使用 Javascript 測試框架Jasmine編寫和執行客戶端程式碼的單元測試。這是系列文章的第八篇,你可以透過閱讀第一篇文章來了解應用的基本構建塊或從頭構建,或者你可以直接前往 Github 上的原始碼(原始碼與第一部分相同,但現在添加了測試)。本文實際上使用的 Spring 或 Spring Security 程式碼很少,但它涵蓋了客戶端測試的一種方式,這種方式可能在通常的 Javascript 社群資源中不容易找到,並且我們認為 Spring 的大多數使用者會對此感到舒適。
與本系列其餘部分一樣,構建工具對 Spring 使用者來說是典型的,但對經驗豐富的前端開發者來說則不然。因此,我們尋求可以使用 Java IDE 並透過熟悉的 Java 構建工具在命令列上使用的解決方案。如果你已經瞭解 Jasmine 和 Javascript 測試,並且樂於使用基於 Node.js 的工具鏈(例如 npm
、grunt
等),那麼你可能完全可以跳過本文。如果你更習慣於 Eclipse 或 IntelliJ,並且希望前端和後端使用相同的工具,那麼本文將對你有所幫助。當我們需要命令列時(例如用於持續整合),我們在示例中使用 Maven,但 Gradle 使用者可能會發現相同的程式碼很容易整合。
提醒:如果你在使用示例應用程式學習本節內容,請務必清除瀏覽器的 cookie 和 HTTP Basic 憑據快取。在 Chrome 中,針對單個伺服器的最佳方法是開啟一個新的隱身視窗。
我們在“basic”應用中的“home”控制器非常簡單,因此對其進行全面測試不會花費太多精力。以下是程式碼(hello.js
)的提醒
angular.module('hello', []).controller('home', function($scope, $http) {
$http.get('resource/').success(function(data) {
$scope.greeting = data;
})
});
我們面臨的主要挑戰是在測試中提供 $scope
和 $http
物件,以便我們能夠斷言它們在控制器中的使用方式。實際上,即使在我們面對這個挑戰之前,我們需要能夠建立一個控制器例項,以便測試它載入時會發生什麼。以下是你可以做到這一點的方法。
建立一個新檔案 spec.js
並將其放在 "src/test/resources/static/js" 中
describe("App", function() {
beforeEach(module('hello'));
var $controller;
beforeEach(inject(function($injector) {
$controller = $injector.get('$controller');
}));
it("loads a controller", function() {
var controller = $controller('home')
});
}
在這個非常基礎的測試套件中,我們有 3 個重要元素
我們使用一個函式 describe()
來描述被測試的物件(在本例中是“App”)。
在該函式內部,我們提供了幾個 beforeEach()
回撥,其中一個載入 Angular 模組“hello”,另一個建立一個控制器工廠,我們稱之為 $controller
。
行為透過呼叫 it()
來表達,我們在其中用文字說明期望是什麼,然後提供一個函式來做出斷言。
這裡的測試函式非常微不足道,它實際上甚至沒有做出斷言,但它確實建立了“home”控制器的一個例項,所以如果建立失敗,測試就會失敗。
注意:“src/test/resources/static/js”是在 Java 應用中放置測試程式碼的邏輯位置,儘管也可以放在“src/test/javascript”。我們稍後將看到為什麼將其放在測試 classpath 中有意義(實際上,如果你習慣於 Spring Boot 的約定,你可能已經明白了)。
現在我們需要一個用於此 Javascript 程式碼的驅動程式,形式是一個可以在瀏覽器中載入的 HTML 頁面。建立一個名為“test.html”的檔案並將其放在“src/test/resources/static”中
<!doctype html>
<html>
<head>
<title>Jasmine Spec Runner</title>
<link rel="stylesheet" type="text/css"
href="/webjars/jasmine/2.0.0/jasmine.css">
<script type="text/javascript" src="/webjars/jasmine/2.0.0/jasmine.js"></script>
<script type="text/javascript"
src="/webjars/jasmine/2.0.0/jasmine-html.js"></script>
<script type="text/javascript" src="/webjars/jasmine/2.0.0/boot.js"></script>
<!-- include source files here... -->
<script type="text/javascript" src="/js/angular-bootstrap.js"></script>
<script type="text/javascript" src="/js/hello.js"></script>
<!-- include spec files here... -->
<script type="text/javascript"
src="/webjars/angularjs/1.3.8/angular-mocks.js"></script>
<script type="text/javascript" src="/js/spec.js"></script>
</head>
<body>
</body>
</html>
HTML 內容為空,但它載入了一些 Javascript,並且一旦所有指令碼都執行完畢,它將具有一個 UI。
首先,我們從 /webjars/**
載入所需的 Jasmine 元件。我們載入的這 4 個檔案僅僅是樣板程式碼 - 你可以對任何應用做同樣的事情。為了在測試執行時使這些檔案可用,我們需要將 Jasmine 依賴項新增到我們的“pom.xml”中
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jasmine</artifactId>
<version>2.0.0</version>
<scope>test</scope>
</dependency>
然後是應用特定的程式碼。我們前端的主要原始碼是“hello.js”,因此我們必須載入它,以及它的依賴項“angular-bootstrap.js”(後者由 wro4j maven 外掛建立,因此你需要先成功執行一次 mvn package
才能載入)。
最後,我們需要我們剛剛編寫的“spec.js”及其依賴項(任何未包含在其他指令碼中的依賴項),對於 Angular 應用,這些依賴項幾乎總是包括“angular-mocks.js”。我們從 webjars 載入它,所以你還需要將該依賴項新增到“pom.xml”中
<dependency>
<groupId>org.webjars</groupId>
<artifactId>angularjs</artifactId>
<version>1.3.8</version>
<scope>test</scope>
</dependency>
注意:angularjs webjar 已經被 wro4j 外掛作為依賴項包含進來,以便它可以構建“angular-bootstrap.js”。這將在不同的構建步驟中使用,所以我們需要再次包含它。
要執行我們的“test.html”程式碼,我們需要一個小型應用(例如在“src/test/java/test”中)
@SpringBootApplication
@Controller
public class TestApplication {
@RequestMapping("/")
public String home() {
return "forward:/test.html";
}
public static void main(String[] args) {
new SpringApplicationBuilder(TestApplication.class).properties(
"server.port=9999", "security.basic.enabled=false").run(args);
}
}
TestApplication
是純粹的樣板程式碼:所有應用都可以以相同的方式執行測試。你可以在 IDE 中執行它並訪問 https://:9999 來檢視 Javascript 執行情況。我們提供的一個 @RequestMapping
只是讓首頁顯示我們的測試 HTML。所有(一個)測試都應該是綠色的。
從這裡開始,你的開發工作流程將是修改 Javascript 程式碼,然後在瀏覽器中重新載入測試應用以執行測試。如此簡單!
為了將規範改進到生產級別,我們需要實際斷言控制器載入時發生的事情。由於它會呼叫 $http.get()
,我們需要模擬該呼叫,以避免僅為了單元測試而不得不執行整個應用。為此,我們使用 Angular 的 $httpBackend
(在“spec.js”中)
describe("App", function() {
beforeEach(module('hello'));
var $httpBackend, $controller;
beforeEach(inject(function($injector) {
$httpBackend = $injector.get('$httpBackend');
$controller = $injector.get('$controller');
}));
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
it("says Hello Test when controller loads", function() {
var $scope = {};
$httpBackend.expectGET('resource/').respond(200, {
id : 4321,
content : 'Hello Test'
});
var controller = $controller('home', {
$scope : $scope
});
$httpBackend.flush();
expect($scope.greeting.content).toEqual('Hello Test');
});
})
這裡新增的部分是
在 beforeEach()
中建立 $httpBackend
。
新增一個新的 afterEach()
來驗證後端的狀體。
在測試函式中,我們在建立控制器之前為後端設定期望,告訴它期望呼叫 'resource/',以及響應應該是什麼。
我們還添加了一個 jasmine 的 expect()
呼叫來斷言結果。
無需啟動和停止測試應用,此測試現在應在瀏覽器中顯示綠色。
能夠在瀏覽器中執行規範非常棒,因為現代瀏覽器內建了優秀的開發者工具(例如 Chrome 中的 F12)。你可以設定斷點並檢查變數,還可以在即時伺服器中重新整理檢視以重新執行測試。但這並不能幫助你進行持續整合:為此,你需要一種從命令列執行測試的方法。無論你喜歡使用哪種構建工具,都有相應的工具可用,但由於我們在這裡使用 Maven,我們將向“pom.xml”新增一個外掛
<plugin>
<groupId>com.github.searls</groupId>
<artifactId>jasmine-maven-plugin</artifactId>
<version>2.0-alpha-01</version>
<executions>
<execution>
<goals>
<goal>test</goal>
</goals>
</execution>
</executions>
</plugin>
此外掛的預設設定無法與我們已經建立的靜態資源佈局配合使用,因此我們需要進行一些配置
<plugin>
...
<configuration>
<additionalContexts>
<context>
<contextRoot>/lib</contextRoot>
<directory>${project.build.directory}/generated-resources/static/js</directory>
</context>
</additionalContexts>
<preloadSources>
<source>/lib/angular-bootstrap.js</source>
<source>/webjars/angularjs/1.3.8/angular-mocks.js</source>
</preloadSources>
<jsSrcDir>${project.basedir}/src/main/resources/static/js</jsSrcDir>
<jsTestSrcDir>${project.basedir}/src/test/resources/static/js</jsTestSrcDir>
<webDriverClassName>org.openqa.selenium.phantomjs.PhantomJSDriver</webDriverClassName>
</configuration>
</plugin>
注意,webDriverClassName
被指定為 PhantomJSDriver
,這意味著你需要在執行時將 phantomjs
新增到你的 PATH
中。這在 Travis CI 中開箱即用,並且在 Linux、MacOS 和 Windows 中需要簡單的安裝 - 你可以下載二進位制檔案或使用包管理器,例如 Ubuntu 上的 apt-get
。原則上,任何 Selenium web 驅動程式都可以在這裡使用(預設是 HtmlUnitDriver
),但對於 Angular 應用來說,PhantomJS 可能是最好的選擇。
我們還需要使 Angular 庫對外掛可用,以便它可以載入“angular-mocks.js”依賴項
<plugin>
...
<dependencies>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>angularjs</artifactId>
<version>1.3.8</version>
</dependency>
</dependencies>
</plugin>
就這樣。又是樣板程式碼(所以如果你想在多個專案之間共享程式碼,可以將其放在父 pom 中)。只需在命令列上執行它
$ mvn jasmine:test
測試也作為 Maven “test”生命週期的一部分執行,因此你只需執行 mvn test
即可執行所有 Java 測試和 Javascript 測試,非常順暢地融入你現有的構建和部署週期。以下是日誌
$ mvn test
...
[INFO]
-------------------------------------------------------
J A S M I N E S P E C S
-------------------------------------------------------
[INFO]
App
says Hello Test when controller loads
Results: 1 specs, 0 failures
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 21.064s
[INFO] Finished at: Sun Apr 26 14:46:14 BST 2015
[INFO] Final Memory: 47M/385M
[INFO] ------------------------------------------------------------------------
Jasmine Maven 外掛還帶有一個目標 mvn jasmine:bdd
,它可以執行一個伺服器,你可以在瀏覽器中載入該伺服器來執行測試(作為上面 TestApplication
的替代方案)。
能夠在現代 Web 應用中執行 Javascript 單元測試非常重要,這是我們在本系列中一直忽視(或迴避)的話題。在本篇中,我們介紹瞭如何編寫測試、如何在開發時執行測試以及更重要的是,如何在持續整合環境中執行測試的基本要素。我們採用的方法並不適合所有人,所以如果採用不同的方式,請不要感到不好,但請確保你具備所有這些要素。我們這裡介紹的方法對於傳統的 Java 企業開發者來說可能會感到舒適,並且能夠很好地與他們現有的工具和流程整合,所以如果你屬於這一類,我希望你會發現它是一個有用的起點。網際網路上有很多關於使用 Angular 和 Jasmine 進行測試的更多示例,但首先可以檢視本系列中的“single”示例,其中包含一些更新的測試程式碼,這些程式碼比本文中“basic”示例所需的程式碼要複雜一些。