play手把手教你创建一个博客项目-10.完整的应用程序测试

时间:2019-05-15 00:38:48下载本文作者:会员上传
简介:写写帮文库小编为你整理了多篇相关的《play手把手教你创建一个博客项目-10.完整的应用程序测试》,但愿对你工作学习有帮助,当然你在写写帮文库还可以找到更多《play手把手教你创建一个博客项目-10.完整的应用程序测试》。

第一篇:play手把手教你创建一个博客项目-10.完整的应用程序测试

10.完整的应用程序测试

现在,我们已经结束了博客引擎的编码工作,但对项目来说还没有完成,为了让我们的代码能够完全正确的工作,我们还需要对项目进行测试。

当然,我们之前已经为yabe的模型层功能书写的单元测试,并且确信博客引擎的核心功能已经进行了完好的测试,但是对于一个web应用程序来说模型层只是其中的一部分,我们还需要确定web接口能否按预期的目标一样正常工作。也就是说还需要测试yabe博客引擎的控制器层,甚至需要对UI自身进行测试,比如我们的JavaScript代码。

测试控制器部分

Play提供了一种功能,就是使用JUnit来直接测试应用程序的控制器。我们把这些测试叫做‘功能性测试’,这是因为我们打算测试web应用程序的完整功能。

基本上,一个功能性测试将直接调用Play的ActionInvoker,和一个HTTP请求相似。因此我们需要给出一个HTTP方法、一个URI和多个HTTP参数。Play之后会路由这些请求,调用相应的action,并且回发到填写的响应(filled response)。之后,你就可以对之进行分析,以检查响应内容是否你所预期的。接下来让我们书写第一个功能性测试代码,打开 yabe/test/ApplicationTest.java单元测试: import org.junit.*;import play.test.*;import play.mvc.*;import play.mvc.Http.*;import models.*;

public class ApplicationTest extends FunctionalTest {

@Test public void testThatIndexPageWorks(){ Response response = GET(“/”);assertIsOk(response);assertContentType(“text/html”, response);assertCharset(“utf-8”, response);} } 现在看,它还是一个标准的JUnit测试。请注意,在这里我们使用Play的 FunctionalTest超类,主要是为了得到所有可用的工具。这个测试只对应用程序的主页进行了测试(/ URL渲染一个HTML响应,以‘200 OK’作为状态代码)。接下来,我们将检查管理区域(administration area)的安全工作能否正常工作。在ApplicationTest.java里添加下面这个新测试: „ @Test public void testAdminSecurity(){ Response response = GET(“/admin”);assertStatus(302, response);assertHeaderEquals(“Location”, “/login”, response);} „

现在,用play test命令把yabe应用程序运行于测试模式,打开http://localhost:9000/@tests, 选择ApplicationTest.java测试并运行。是绿色的吗? 当然!通过这种方式,我们可以对所有的应用程序功能性进行测试,但把这用于测试一个基于html的web应用程序时,这并不是最好的方式。对于我们的博客引擎项目来说,直接在真实的浏览器进行测试可能会更好。这就是play的‘Selenium tests’测试要干的事。

这种基于“功能性测试”的JUnit仍旧很有用,特别是用于测试一个返回非html响应(比如JSON或XML)的Web services时。

书写Selenium测试代码 Selenium 是一个专用于测试web应用程序的测试工具。这个工具最酷的就是Selenium允许我们在一个浏览器里直接运行测试套件,由于它使用的是真实的浏览器,因此,我们可以确定测试通过后,项目就可以在生产环境下完美的运行。一个Selenium测试套件就是一个特殊的html文件。HTML syntax required by Selenium必须使用的HTML语句比较单调(使用HTML表格元素进行数据格式化显示),好消息是play将使用play模板引擎和一系列支持简单Selenium表示语法的标签来帮助你生成这些元素)。使用模板最有趣的特点是你根本不需要‘static scenarios’,并且可以使用play模板强大的功能(如循环、条件块)来书写更复杂的测试。

然而,你仍旧可以继续在模板里使用原始的HTML Selenium语法,如果需要的话,还可以忘记特定的Selenium标签。如果你使用多个用于生成test scenarios(比如Selenium IDE)的Selenium工具中的一个,这将变得非常有趣。

新创建的play应用程序的默认测试套件已经包含了一个Selenium测试,打开yabe/test/Application.test.html文件:

*{ You can use plain Selenium commands using the selenium tag }*

#{selenium} // Open the home page, and check that no error occurred open('/')waitForPageToLoad(1000)assertNotTitle('Application error')#{/selenium} 运行这个测试应该不会有任何问题。它只打开了主页,并检测页面内容是否包含了 ‘Application error’文本。

然而,和任何复杂的测试一样,在导航到应用程序并进行测试之前,你需要设置一系列众所周知的数据,我们当然需要重用fixture概念,并且在开始测试之前使用yabe/test/data.yml文件,#{fixture /}标签导入这些测试数据: #{fixture delete:'all', load:'data.yml' /}

#{selenium} // Open the home page, and check that no error occurred open('/')waitForPageToLoad(1000)assertNotTitle('Application error')#{/selenium} 另外一个重要的事情就是我们要在测试启动时检查我们是否有一个最新的用户session。这个session将存储在浏览器的临时cookie里,你应该在两个连续的 测试运行操作期间保持同一个session,因此,让我们用一个特定的命令开始测试:

#{fixture delete:'all', load:'data.yml' /}

#{selenium} clearSession()

// Open the home page, and check that no error occurred open('/')waitForPageToLoad(1000)assertNotTitle('Application error')#{/selenium} 运行这个测试,并确定没有错误发生,结果应该是绿色的。

接下来我们将书写很特殊的测试,测试打开主页后检查默认的博文是否显示出来:

#{fixture delete:'all', load:'data.yml' /}

#{selenium 'Check home page'} clearSession()

// Open the home page open('/')

// Check that the front post is present assertTextPresent('About the model layer')assertTextPresent('by Bob, 14 Jun 09')assertTextPresent('2 comments , latest by Guest')assertTextPresent('It is the domain-specific representation')

// Check older posts assertTextPresent('The MVC application')assertTextPresent('Just a test of YABE')#{/selenium} 在这里,我们使用了标准的Selenium语法,它叫Selenese。运行它(你可以运行于一个不同的浏览器窗口里)。我们现在就可以测试评论窗体了,只需要添加一个 #{selenium /} 到模板里即可:

#{selenium 'Test comments'}

// Click on 'The MVC application post' clickAndWait('link=The MVC application')assertTextPresent('The MVC application')assertTextPresent('no comments')

// Post a new comment type('content', 'Hello')clickAndWait('css=input[type=submit]')

// Should get an error assertTextPresent('no comments')assertTextPresent('Author is required')type('author', 'Me')clickAndWait('css=input[type=submit]')

// Check assertTextPresent('Thanks for posting Me')assertTextPresent('1 comment')assertTextPresent('Hello')#{/selenium} 再次才能,哦,失败了!这里有一个严重的问题出现。我们事实上不能正确测试captcha验证码机制,因此,我们必须搞一些欺骗手段。在测试模式下,我们将验证任何代码作为一个正确的验证码。我们知道当框架a.We know that we’re in test mode when the framework id is test.So let’s modify the postComment action in the yabe/app/controllers/Application.java file to skip this validation in test mode: „

if(!Play.id.equals(“test”)){ validation.equals(code, Cache.get(randomID)).message(“Invalid code.Please type it again”);} „

Now just modify the test case to type any code in the text field, as is: „

type('author', 'Me')type('code', 'XXXXX')clickAndWait('css=input[type=submit]')„

And now run the test again, it should work.Measuring code coverage Of course we haven’t written all required test cases for the application.But it’s enough for this tutorial.Now in a real-world project, how can we know if we have written enough test cases? We need something called ‘code coverage’.The Cobertura module generates code coverage reports using the Cobertura tool.Install the module using the install command: play install cobertura-{version} We need to enable this module only for test mode.So add this line to the application.conf file, and restart the application in test mode.# Import the cobertura module in test mode %test.module.cobertura=${play.path}/modules/cobertura Now reopen the browser at the http://localhost:9000/@tests URL, select all tests and run them.All should be green.When all tests are passed, stop the application and cobertura will then generate the code coverage report.You can then open the yabe/test-result/code-coverage/index.html in your browser and check the report.If you start the application again, you can also view it at http://localhost:9000/@cobertura.As you see we’re far from testing all of the application’s cases.A good testing suite should approach 100%, even if it is of course nearly impossible to check all the code.Typically because we often need to hack in test mode, like we did for the captcha.

第二篇:play手把手教你创建一个博客项目-08.添加身份认证

08.添加身份认证

在上一节是,我们为应用程序添加了管理区域(administration area)功能,现在我们将在这些管理区域中插入一些身份认证功能。幸运的是,play已经为这个功能准备了一个模块,这个模块叫Secure(安全)。

在程序里允许使用Secure模块

在yabe/conf/application.conf文件里允许使用Secure模块,并重启程序: # Import the secure module module.secure=${play.path}/modules/secure 重启后,play应用在控制台显示模块已经成功启动的相关信息。

Secure模块带有一系列默认的路由,需要在yabe/conf/routes里引入(或定义自己的路由也行): # Import Secure routes * / module:secure 保护admin(此处指需要身份认证的)控制器

安全模块提供了一个controllers.Secure控制器,它声明了所有可能用到的拦截器。当然,我们可以以继承这个控制器的方法获得其拦截器,但是java只允许单继承,这就导致了一些问题。

为了避免单继承带来的限制,我们可以用@With来注释admin控制器,以告诉play去调用相应的拦截器:

package controllers;

import play.*;import play.mvc.*;

@With(Secure.class)public class Posts extends CRUD { } 同样用于Comments, Users和Tags控制器。

Now if you try to access any admin action, you should get a log-in page: 事实上,现在你就可以试着输入任意username/password对看看,它其实并没有对身份进行认证。

定制身份认证处理

应用程序必须提供一个controllers.Secure.Security实例来定制身份认证处理。通过继承这个类来创建我们自己版本的Secure类,我们可以指定如何对用户身份进行认证。

创建yabe/app/controllers/Security.java文件,重写authenticate()方法: package controllers;

import models.*;

public class Security extends Secure.Security {

static boolean authenticate(String username, String password){ return true;} } 既然我们已经拥有了User对象,那么就非常容易实现这个方法: static boolean authenticate(String username, String password){ return User.connect(username, password)!= null;} 现在打开http://localhost:9000/logout进行登录尝试,用户名和密码在initial-data.yml文件里定义,比如bob@gmail.com/secret。

重构管理区域(administration area)

我们之前已经使用CRUD模块来实现管理区域,但是这个管理区域仍然没有集成到博客UI里,接下来我们将在一个新的管理区域上开始工作。这个新的管理区域允许每个作者访问他自己的博客。而超级用户则继续使用完整功能的管理区域。

接下来,让我们为管理部分创建一个新Admin控制器: package controllers;

import play.*;import play.mvc.*;

import java.util.*;

import models.*;

@With(Secure.class)public class Admin extends Controller {

@Before static void setConnectedUser(){ if(Security.isConnected()){ User user = User.find(“byEmail”, Security.connected()).first();renderArgs.put(“user”, user.fullname);} }

public static void index(){ render();} } 重构路由定义yabe/conf/routes: # Administration GET /admin/? Admin.index * /admin module:crud 请注意路由的顺序,第一行就匹配了的http请求相应的action会率先使用,同时忽略在其之下的路由配置。也就是说Admin控制器必须位于第二行之上,第二条路由将匹配所有其他的/admin请求,用于调用CRUD模块页面,否则/admin/将映射到CRUD.index而不是Admin.index。

现在把yabe/app/views/main.html模块里的 ‘Log in to write something’文本修改到Admin.index控制器action:

最后一件事就是为yabe/app/views/Admin/index.html模板文件完成所有的填充工作,让我们从简单的开始: Welcome ${user}!现在回到主页,单击 ‘Log in to write something’链接就回进入样的管理区域页面: 非常好!但是既然我们已经有几个管理区域的页面,那么,我们就应该有一个超级模板以重用代码,让我们创建一个yabe/app/views/admin.html模板: Administration

#{get 'moreStyles' /}

yabe.administration

第三篇:play手把手教你创建一个博客项目-03.构建第一个页面

03.构建第一个页面

之前,我们已经编写好了数据模型,是时候为应用程序创建第一个页面了。这个页面只显示当前发表的博文完整内容,同时显示之前发表的博文列表。下面是该页面结构示意图:

在启动时加载默认数据

事实上,在编写第一个页面之前,我们还需要做一些工作。在一个没有任何测试数据的页面上进行工作并不太好,你甚至不能进行任何测试。

为博客程序注入测试数据的一条途径就是在应用程序启动时加载一个固定文件。为了实现这个目的,我们将创建一个引导任务(Bootstrap Job)。一个play job 任务就是一在没有任何http请求的情况下执行一些特定工作,比如在应用程序启动时或指定时间间隔时使用CRON任务。

接下来,让我们创建/yabe/app/Bootstrap.java job文件,使用Fixtures加载一系列默认数据:

import play.*;import play.jobs.*;import play.test.*;

import models.*;

@OnApplicationStart public class Bootstrap extends Job {

public void doJob(){ // 检查数据库是否为空 if(User.count()== 0){ Fixtures.loadModels(“initial-data.yml”);} } } 在这里,我们使用@OnApplicationStart来注释这个Job,用于告诉play我们打算在应用程序启动时同步运行这个任务。

事实上,在DEV和PROD模式下,这个任务的运行情况有所不同。在DEV模式下,play会等待第一个请求达到时才运行任务。因此这个任务会在第一个请求到达时才同步执行,也就是说,如果这个任务失败,你会在浏览器里看到错误消息。在PROD模式里,这个任务将会在应用程序启动时就执行(与play run命令同步),以防止应用程序在启动时发生错误。

你必须在yabe/conf/下创建一个initial-data.yml文件。当然,你也可以重用我们之前在data.yml里定义的内容。

博客主页

这次,我们将真正开始编写主页代码。

还记得程序的第一个页面是如何显示的吗?首先是在routes文件里指定/ URL 将调用controllers.Application.index()action方法,然后这个index()调用render()方法渲染/yabe/app/views/Application/index.html模板。我们将保留这些组件,但是我们会在其中添加代码来加载博文列表并显示。打开/yabe/app/controllers/Application.java控制器并修改index()action来加载博文列表:

package controllers;

import java.util.*;

import play.*;import play.mvc.*;

import models.*;

public class Application extends Controller {

public static void index(){ Post frontPost = Post.find(“order by postedAt desc”).first();List

olderPosts = Post.find(“order by postedAt desc”).from(1).fetch(10);render(frontPost, olderPosts);} } 看到我们是如何向render方法传递对象的吗?通过这种方式,我们就可以在模板里使用相同的名称来访问这些对象了。在上面的代码里,我们在模板里就可以直接使用变量frontPost和olderPosts。

打开/yabe/app/views/Application/index.html并修改,用于显示这些对象: #{extends 'main.html' /} #{set title:'Home' /}

#{if frontPost}

${frontPost.title}

by ${frontPost.author.fullname} ${frontPost.postedAt.format('MMM dd')}  |  ${frontPost.comments.size()?: 'no'} comment${frontPost.comments.size().pluralize()} #{if frontPost.comments} , latest by ${frontPost.comments[-1].author} #{/if}

第四篇:play手把手教你创建一个博客项目-09.创建定制编辑区域

09.创建定制编辑区域

在之前的教程中,我们已经创建一个管理区域(administration area),并准备好了‘My posts’部分。这个页面将为每位作者提供一个属于他自己博文的列表,并且可以对这些博文进行编辑或创建新的博文。

我们可以重用CRUD模块作为这个页面的基础,但在这里,我们将以此为基础(scratch,起跑线,基础)创建一些东西,我们需要在这些屏幕里提供许多个性化的东西。

从用户博文列表开始

在这里,我们只需得到并显示当前登录用户的博文。非常简单,让我们开始增强Admin.index action的功能: public static void index(){ String user = Security.connected();List

posts = Post.find(“author.email”, user).fetch();render(posts);} 并且完成yabe/app/views/Admin/index.html模板: #{extends 'admin.html' /}

Welcome ${user}, you have written ${posts.size()?: 'no'} ${posts.pluralize('post', 'posts')} so far

#{list items:posts, as:'post'}

${post.title}

#{/list}

+ write a new post

第一个屏幕就准备好了: 接着写‘写一篇新博文’页面

我们将创建一个窗体来实现创建一篇新博文。通常情况下,在窗体操作时你需要两个actions:一个用于显示窗体,另一个用于处理窗体提交的信息。让我们创建这个新的Admin.form和Admin.save actions,以用于显示和处理窗体提交信息:

在abe/conf/routes里添加如下路由:

GET /admin/new Admin.form POST /admin/new Admin.save 接着在Admin.java控制器里添加form()和save()actions: public static void form(){ render();}

public static void save(){ // Not implemented yet } 接着,你就必须创建yabe/app/views/Admin/form.html模板:

#{extends 'admin.html' /}

Write, a new post

#{form @save()}

#{ifErrors}

Please correct these errors.

#{/ifErrors}

#{field 'title'} #{error 'post.title' /} #{/field}

#{field 'content'} #{error 'post.content' /} #{/field}

#{field 'tags'} #{/field}

#{/form} 最后编辑yabe/app/views/Admin/index.html模板来链接到 Write a new post 按钮到这个窗体:

...

+ write a new post

...检测一下结果: 接下来,我们必须完成Admin.save action,以用于处理当前窗体提交的数据。action将创建一个新的Post对象,把tag(特征)列表转换到真实的Tag对象集合,验证所有的字段并存储他们。如果有任何问题发生,那么还能够重新显示窗体,并标出错误位置:

public static void save(String title, String content, String tags){ // Create post User author = User.find(“byEmail”, Security.connected()).first();Post post = new Post(author, title, content);// Set tags list for(String tag : tags.split(“s+”)){ if(tag.trim().length()> 0){ post.tags.add(Tag.findOrCreateByName(tag));} } // Validate validation.valid(post);if(validation.hasErrors()){ render(“@form”, post);} // Save post.save();index();} 在这里,我们使用render(“@form”)作为render(“Admin/form.html”)的快捷方式,它只告诉play去使用窗体action的默认模板。测试下看看!

为正在编辑的Posts重用这些填充数据

我们已经定义了HTML窗体并且让Java action能够创建一个新的博客Post,但是我们同时也需要允许编辑存在的博文,因此我们可以很容易地重用相同的代码,使我们在编辑现有博文时的工作量尽量减少: 首先我们需要让Admin.form找回存在的Post: public static void form(Long id){ if(id!= null){ Post post = Post.findById(id);render(post);} render();} 正如你所看到的一样,我们使之成为可选项(通过id!=null),因此当id参数已经填值后,同样的action将用于找回存在的博文。因此,我们现在可以把主屏幕的博文列表链接到编辑窗体了。编辑yabe/app/views/Admin/index.html模板:

#{extends 'admin.html' /}

Welcome ${user}, you have written ${posts.size()?: 'no'} ${posts.pluralize('post', 'posts')} so far

#{list items:posts, as:'post'}

${post.title}

#{/list}

+ write a new post

非常好,很容易就实现了,但这里还有一个小问题。如果你看一下通过Router为这些链接生成的真实的URL,你就会发现如下样式的URL: /admin/new?id=3 它们能正常工作,但是并不太漂亮。OK,让我们对其进行修改,如果id参数提交上来,我们将使用一个不同的URL范示,路由定义如下: GET /admin/myPosts/{id} Admin.form GET /admin/new Admin.form 正如你看到的一样,我们在旧的路由之前定义了这些路由,因此它们具有更好的优先级。这也就是说,如果一个id参数被提交,play将率先使用这个URL范示,如果没有提交id参数,那么play将继续使用旧的路由。刷新My posts页面看看更贾漂亮的URL链接效果。

接下来我们需要修改yabe/app/views/Admin/form.html模板,使其更好: #{extends 'admin.html' /}

#{ifnot post?.id}

Write, a new post

#{/ifnot} #{else}

Edit, this post

#{/else}

#{form @save(post?.id)}

#{ifErrors}

Please correct these errors.

#{/ifErrors}

#{field 'title'} #{error 'post.title' /} #{/field}

#{field 'content'} #{error 'post.title' /} #{/field}

#{field 'tags'} #{/field}

#{/form} 正如你看到的一样,我们已经更新窗体目的action,以方便添加博文ID作为第一个action参数。因此当博文拥有一个id字段集时(意思是博文在系统里已经存在),窗体将发送到Admin.save action进行处理。接下来我们可以修改save()方法来处理创建与编辑两种情况:

public static void save(Long id, String title, String content, String tags){ Post post;if(id == null){ // Create post User author = User.find(“byEmail”, Security.connected()).first();post = new Post(author, title, content);} else { // Retrieve post post = Post.findById(id);// Edit post.title = title;post.content = content;post.tags.clear();} // Set tags list for(String tag : tags.split(“s+”)){ if(tag.trim().length()> 0){ post.tags.add(Tag.findOrCreateByName(tag));} } // Validate validation.valid(post);if(validation.hasErrors()){ render(“@form”, post);} // Save post.save();index();} 使用和之前同样的技巧,让URL更加完美,如果id参数存在,我们就使用优先路由:

POST /admin/myPosts/{id} Admin.save POST /admin/new Admin.save 工作全部完成!现在我们使用同一样action就实现了创建新博文与编辑旧博文的功能,同时完成了管理区域administration area)的编程工作!

第五篇:play手把手教你创建一个博客项目-04.显示并发表评论

04.显示并发表评论

博客的主页已经搞定,接下来,我们将编写博文的详细内容页面。这个页面将显示当前博文的所有评论,页面还将包括一个用于发表新评论的窗体。

创建 ‘show’ action

为了显示博文的详细页面,我们需要在Application控制器里创建一个新的action方法,这个新的action叫做show(): public static void show(Long id){ Post post = Post.findById(id);render(post);} 你可以看到,这个action非常简单。我们在方法签名里声明了一个Long类型的id参数用于自动从http请求的id变量里得到id值。这个参数会从HTTP的请求字符串、URL路径或窗体里获得

如果我们试着从http请求里传递一个错误的id值时,id变量值将为null,这时play将自动添加一个验证错误秋errors容器(变量)里。

这个action将用/yabe/app/views/Application/show.html模板进行显示: #{extends 'main.html' /} #{set title:post.title /}

#{display post:post, as:'full' /} 由于我们在之前已经编写了display标签,因此在这个页面的编码就非常简单了。

为详细页面添加链接

在display标签里,我们让所有的链接为空(使用#)。是时候让这些链接指向Application.show action了。在play里,你可以很容易在一个模板里使用@{...}注释构建链接,这个语法使用了路由的“URL反向查找”功能,用于反向查找指定action的URL。

打开/yabe/app/views/tags/display.html标签并修改: „

${_post.title}

刷新主页,单击博文标题以显示博文内容。非常好,但是这个页面缺少一个返回主页的链接。OK,让我们打开/yabe/app/views/main.html模板,完成标题的链接:

About this blog

${blogTitle}

${blogBaseline}

下载play手把手教你创建一个博客项目-10.完整的应用程序测试word格式文档
下载play手把手教你创建一个博客项目-10.完整的应用程序测试.doc
将本文档下载到自己电脑,方便修改和收藏,请勿使用迅雷等下载。
点此处下载文档

文档为doc格式


声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:645879355@qq.com 进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。

相关范文推荐