更上一層樓
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」中。我們稍後將會看到為什麼將其放在測試類別路徑中是有意義的(實際上,如果您習慣使用 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 中執行它,並造訪 http://localhost: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 driver(預設是 HtmlUnitDriver
),但 PhantomJS 可能是 Angular 應用程式的最佳選擇。
我們還需要讓 Angular library 可供此外掛程式使用,以便它可以載入 "angular-mocks.js" 這個依賴項目。
<plugin>
...
<dependencies>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>angularjs</artifactId>
<version>1.3.8</version>
</dependency>
</dependencies>
</plugin>
就是這樣。 又是樣板程式碼(boilerplate code)(如果您想在多個專案之間共享程式碼,它可以放在父 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
的替代方案)。
能夠為 Javascript 運行單元測試在現代 Web 應用程式中非常重要,並且這是我們在這個系列中迄今為止忽略(或迴避)的一個主題。 在這一期中,我們介紹了如何編寫測試、如何在開發時運行它們,以及更重要的是,如何在持續整合環境中運行它們的基本要素。 我們採用的方法並不適合所有人,所以請不要因為以不同的方式進行而感到難過,但請確保您擁有所有這些要素。 我們在這裡所做的方式對於傳統的 Java 企業開發人員來說可能會感到舒服,並且可以很好地與他們現有的工具和流程整合,因此如果您屬於這一類別,我希望您會發現它作為一個起點很有用。 有關使用 Angular 和 Jasmine 進行測試的更多範例可以在網路上找到,但第一個參考點可能是這個系列中的 "single" 範例,它現在有一些最新的測試程式碼,這些程式碼比我們需要為本文中的 "basic" 範例編寫的程式碼稍微複雜一些。