Gradle包含war
插件用于构建Java Web应用程序的,社区提供了一个出色的gretty
插件,用于在Jetty或Tomcat上测试和部署Web应用程序。本指南演示了如何构建一个简单的Web应用程序以及如何使用该gretty
插件将其部署在Jetty上。您还将学习如何使用Mockito框架为Servlet编写单元测试,以及如何使用gretty
and Selenium 为Web应用程序编写功能测试。
你需要什么
-
约21分钟
-
文本编辑器或IDE
-
Java发行版
-
版本7或更高版本(如果使用Gradle Groovy DSL)
-
版本8或更高版本(如果使用Gradle Kotlin DSL)
-
-
一个Gradle,版本4.10.3或更好
创建Web应用程序的结构
Gradle包含一个war
插件,该插件记录在用户手册的WAR插件一章中。该war
插件扩展了Java插件,以增加对Web应用程序的支持。默认情况下,它使用一个src/main/webapp
与Web相关的资源的文件夹。
用户手册中的“ Web应用程序快速入门”部分仍指Jetty插件,不推荐使用该插件,而推荐使用此处使用的gretty插件。但是,特定于war插件的零件很好,该部分将很快更新。
因此,为名为的项目创建以下文件结构webdemo
:
webdemo/
src/
main/
java/
webapp/
test
java/
任何servlet或其他Java类都会加入src/main/java
,测试将进入src/test/java
,其他Web工件将进入src/main/webapp
。
添加Gradle构建文件
将一个名为build.gradle
(如果使用Groovy DSL)或build.gradle.kts
(如果使用Kotlin DSL)的文件添加到项目的根目录,其内容如下:
plugins {
id 'war'
}
repositories {
jcenter()
}
dependencies {
providedCompile 'javax.servlet:javax.servlet-api:3.1.0'
testCompile 'junit:junit:4.12'
}
- 使用
war
插件 - Servlet API的当前发行版
该war
插件添加配置providedCompile
和providedRuntime
,类似于compile
和runtime
在常规Java应用程序中进行配置,以表示本地所需的依赖关系,但不应将其添加到生成的webdemo.war
文件中。
该plugins
语法用于应用java
和war
插件。两者都不需要版本,因为它们包含在Gradle发行版中。
通过执行以下wrapper
任务为项目生成Gradle包装器是一个好习惯:
$ gradle wrapper --gradle-version=4.10.3
:wrapper
这将产生gradlew
与gradlew.bat
脚本和gradle
如所描述的与包装罐内部夹包装片的用户手册的。
如果您使用的是Gradle 4.0或更高版本,则控制台中的输出可能会比本指南中看到的少。在本指南中,使用命令行上的--console = plain标志显示输出。这样做是为了显示Gradle正在执行的任务.
向项目添加Servlet和元数据
定义Web应用程序元数据有两个选项。在Servlet规范3.0版之前,元数据驻留在项目文件夹中称为部署描述符web.xml
中WEB-INF
。从3.0开始,可以使用注释定义元数据。
org/gradle/demo
在src/main/java
文件夹下面创建一个包文件夹。添加一个servlet文件HelloServlet.java
,内容如下:
package org.gradle.demo;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet(name = "HelloServlet", urlPatterns = {"hello"}, loadOnStartup = 1)
public class HelloServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.getWriter().print("Hello, World!");
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String name = request.getParameter("name");
if (name == null) name = "World";
request.setAttribute("user", name);
request.getRequestDispatcher("response.jsp").forward(request, response);
}
}
- 基于注释的servlet
- GET请求返回一个简单的字符串
- POST请求转发到JSP页面
Servlet使用@WebServlet
注释进行配置。该doGet
方法通过编写“ Hello,World!”来响应HTTP GET请求。字符串输出输出。它通过查找名为的请求参数name
并将其request
作为称为属性添加到,对HTTP POST请求做出反应user
,然后转发到response.jsp
页面。
该
war
插件支持使用较早的web.xml
部署描述符,默认情况下,该描述符应位于WEB-INF
下方的文件夹中src/main/webapp
。随意使用它作为基于注释的方法的替代方法。
现在,您有了一个简单的servlet,它可以响应HTTP GET和POST请求。
将JSP页面添加到演示应用程序
通过在文件夹中创建具有以下内容index.html
的src/main/webapp
文件,将索引页添加到应用程序的根目录:
<html>
<head>
<title>Web Demo</title>
</head>
<body>
<p>Say <a href="hello">Hello</a></p>
<form method="post" action="hello">
<h2>Name:</h2>
<input type="text" id="say-hello-text-input" name="name" />
<input type="submit" id="say-hello-button" value="Say Hello" />
</form>
</body>
</html>
- 链接提交GET请求
- 表单使用POST请求
该index.html
页面使用一个链接向Servlet提交HTTP GET请求,并使用一个表单提交HTTP POST请求。该表单包含一个名为的文本字段name
,该字段可由Servlet在其doPost
方法中进行访问。
doPost
servlet 通过其方法将控制权转发到另一个名为的JSP页面response.jsp
。因此,请在src/main/webapp
其中定义以下名称的文件:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Hello Page</title>
</head>
<body>
<h2>Hello, ${user}!</h2>
</body>
</html>
该response
页面user
从请求中访问了变量,并将其呈现在h2
标签中。
添加gretty
插件并运行应用
该gretty
插件是社区支持的出色插件,可以在Gradle插件存储库中找到https://plugins.gradle.org/plugin/org.akhikhl.gretty
。该插件使在Jetty或Tomcat上运行或测试Web应用程序变得容易。
通过将以下行添加到plugins
构建脚本中的块中,将其添加到我们的项目中。
build.gradle
plugins {
id 'war'
id 'org.gretty' version '2.2.0'
}
- 添加
gretty
插件
该gretty
插件向应用程序添加了大量任务,对于在Jetty或Tomcat环境中运行或测试很有用。现在,您可以使用该appRun
任务将应用构建并部署到默认(Jetty)容器。
appRun
任务
$ ./gradlew appRun
:prepareInplaceWebAppFolder
:createInplaceWebAppFolder UP-TO-DATE
:compileJava
:processResources UP-TO-DATE
:classes
:prepareInplaceWebAppClasses
:prepareInplaceWebApp
:appRun
12:25:13 INFO Jetty 9.2.15.v20160210 started and listening on port 8080
12:25:13 INFO webdemo runs at:
12:25:13 INFO http://localhost:8080/webdemo
Press any key to stop the server.
> Building 87% > :appRun
BUILD SUCCESSFUL
现在,您可以在http://localhost:8080/webdemo上访问该Web应用程序,然后单击链接以执行GET请求或提交表单以执行POST请求。
尽管输出显示Press any key to stop the server
,但Gradle不会拦截标准输入。要停止该过程,请按ctrl-C
。
使用Mockito对Servlet进行单元测试
开源Mockito框架使对Java应用程序进行单元测试变得容易。将Mockito依赖项添加到testCompile
配置下的构建脚本中。
build.gradle
dependencies {
providedCompile 'javax.servlet:javax.servlet-api:3.1.0'
testCompile 'junit:junit:4.12'
testCompile 'org.mockito:mockito-core:2.7.19'
}
添加Mockito 要对Servlet进行单元测试,请
org.gradle.demo
在下方创建一个package文件夹src/test/java
。
添加HelloServletTest.java
具有以下内容的测试类文件:
package org.gradle.demo;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import javax.servlet.RequestDispatcher;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.io.StringWriter;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.*;
public class HelloServletTest {
@Mock private HttpServletRequest request;
@Mock private HttpServletResponse response;
@Mock private RequestDispatcher requestDispatcher;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
@Test
public void doGet() throws Exception {
StringWriter stringWriter = new StringWriter();
PrintWriter printWriter = new PrintWriter(stringWriter);
when(response.getWriter()).thenReturn(printWriter);
new HelloServlet().doGet(request, response);
assertEquals("Hello, World!", stringWriter.toString());
}
@Test
public void doPostWithoutName() throws Exception {
when(request.getRequestDispatcher("response.jsp"))
.thenReturn(requestDispatcher);
new HelloServlet().doPost(request, response);
verify(request).setAttribute("user", "World");
verify(requestDispatcher).forward(request,response);
}
@Test
public void doPostWithName() throws Exception {
when(request.getParameter("name")).thenReturn("Dolly");
when(request.getRequestDispatcher("response.jsp"))
.thenReturn(requestDispatcher);
new HelloServlet().doPost(request, response);
verify(request).setAttribute("user", "Dolly");
verify(requestDispatcher).forward(request,response);
}
}
测试创建模拟对象HttpServletRequest
,HttpServletResponse
以及RequestDispatcher
类。对于doGet
测试,将创建一个PrintWriter
使用a的a StringWriter
,并将模拟请求对象配置为在getWriter
调用该方法时将其返回。调用该doGet
方法后,测试将检查返回的字符串是否正确。
对于发布请求,将模拟请求配置为返回给定名称(如果存在)或getRequestDispatcher
返回null,并且该方法返回关联的模拟对象。调用该doPost
方法将执行请求。然后Mockito验证是否setAttribute
使用适当的参数在模拟响应上调用了该方法,并验证了forward
在请求分派器上是否调用了该方法。
现在,您可以将Gradle与test
任务(或任何build
依赖于该任务的任务,例如)一起使用来测试servlet 。
$ ./gradlew build
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:war
:assemble
:compileTestJava
:processTestResources UP-TO-DATE
:testClasses
:test
:check
:build
BUILD SUCCESSFUL
可以通过build/reports/tests/test/index.html
常规方式访问测试输出。您应该得到类似于以下结果:

添加功能测试
该gretty
插件与Gradle结合使用,可以轻松地向Web应用程序添加功能测试。这样做,将以下行添加到您的生成脚本:
build.gradle
gretty {
integrationTestTask = 'test'
}
// ... rest from before ...
dependencies {
providedCompile 'javax.servlet:javax.servlet-api:3.1.0'
testCompile 'junit:junit:4.12'
testCompile 'org.mockito:mockito-core:2.7.19'
testCompile 'io.github.bonigarcia:webdrivermanager:1.6.1'
testCompile 'org.seleniumhq.selenium:selenium-java:3.3.1'
}
- 告诉gretty启动和停止测试服务器
- 自动安装浏览器驱动程序
- 使用硒进行功能测试
该gretty
插件需要知道哪些任务需要服务器的启动和停止。通常,这是分配给您自己的任务的,但是为了使事情简单,只需使用现有test
任务即可。
Selenium是用于编写功能测试的流行开源API。2.0版基于WebDriver API。最新版本要求测试人员为他们的浏览器下载并安装WebDriver版本,这很繁琐且难以自动化。该WebDriverManager项目可以很容易地让摇篮句柄,为您办理。
在src/test/java
目录中,将以下功能测试添加到您的项目中:
package org.gradle.demo;
import io.github.bonigarcia.wdm.ChromeDriverManager;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import static org.junit.Assert.assertEquals;
public class HelloServletFunctionalTest {
private WebDriver driver;
@BeforeClass
public static void setupClass() {
ChromeDriverManager.getInstance().setup();
}
@Before
public void setUp() {
driver = new ChromeDriver();
}
@After
public void tearDown() {
if (driver != null)
driver.quit();
}
@Test
public void sayHello() throws Exception {
driver.get("http://localhost:8080/webdemo");
driver.findElement(By.id("say-hello-text-input")).sendKeys("Dolly");
driver.findElement(By.id("say-hello-button")).click();
assertEquals("Hello Page", driver.getTitle());
assertEquals("Hello, Dolly!", driver.findElement(By.tagName("h2")).getText());
}
}
- 必要时下载并安装浏览器驱动程序
- 启动浏览器自动化
- 完成后关闭浏览器
- 使用Selenium API运行功能测试
此测试的WebDriverManager部分检查二进制文件的最新版本,并在不存在该二进制文件时下载并安装它。然后,该sayHello
测试方法将Chrome浏览器驱动到我们应用程序的根目录,填写输入文本字段,单击按钮,然后验证目标页面的标题以及h2
标签中是否包含预期的字符串。
WebDriverManager系统支持Chrome,Opera,Internet Explorer,Microsoft Edge,PhantomJS和Firefox。查看项目文档以获取更多详细信息。
运行功能测试
使用以下test
任务运行测试:
$ ./gradlew test
:prepareInplaceWebAppFolder UP-TO-DATE
:createInplaceWebAppFolder UP-TO-DATE
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:prepareInplaceWebAppClasses UP-TO-DATE
:prepareInplaceWebApp UP-TO-DATE
:compileTestJava UP-TO-DATE
:processTestResources UP-TO-DATE
:testClasses UP-TO-DATE
:appBeforeIntegrationTest
12:57:56 INFO Jetty 9.2.15.v20160210 started and listening on port 8080
12:57:56 INFO webdemo runs at:
12:57:56 INFO http://localhost:8080/webdemo
:test
:appAfterIntegrationTest
Server stopped.
BUILD SUCCESSFUL
该gretty
插件会在默认端口上启动Jetty 9的嵌入式版本,执行测试,然后关闭服务器。如果您观看了,您会看到Selenium系统打开一个新的浏览器,访问该站点,填写表格,单击按钮,检查新页面,最后关闭浏览器。
集成测试通常通过创建单独的源集和专用任务进行处理,但这超出了本指南的范围。有关详细信息,请参见Gretty文档。
摘要
在本指南中,您学习了如何:
-
使用
war
Gradle构建中的插件定义Web应用程序 -
将servlet和JSP页面添加到Web应用程序
-
使用
gretty
插件部署应用程序 -
使用Mockito框架对Servlet进行单元测试
-
使用
gretty
和Selenium 对Web应用程序进行功能测试