咱们已经完成了咱们想要建立的博客引擎。不过这个项目还没有彻底结束。为了保证代码的质量,咱们须要添加更多的测试。css
固然咱们已经完成了测试全部模型层的功能。因此博客引擎的核心功能已经被测试过了。可是一个Web应用并不仅有模型层。咱们须要确保Web交互界面是否无缺。这须要测试引擎的控制层。可是咱们甚至须要测试UI界面,好比咱们的Javascript代码。html
经过JUnit,你能够直接测试应用的控制层。咱们将这些测试称之为功能测试。由于咱们会测试应用完整的功能。java
一个功能测试会调用Play的ActionInvoker
,模拟一个HTTP请求。咱们须要提供HTTP方法,一个URI和HTTP参数。Play会根据这个请求,找到路由,接着调用对应的action,最后返回一个响应。你能够分析它,看看响应是否在乎料之中。segmentfault
先写第一个功能测试。打开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’状态码)。安全
如今咱们将检测管理面板的安全是否禁得住考验。给ApplicationTest.java
添加新的测试:cookie
… @Test public void testAdminSecurity() { Response response = GET("/admin"); assertStatus(302, response); assertHeaderEquals("Location", "/login", response); } …
使用play test
命令运行你的yabe应用,打开http://localhost:9000/@tests,选择ApplicationTest.java
用例,而后运行它。mvc
变绿了吗?app
如今,咱们能够对应用的所有功能进行一样的测试,但这并非测试一个基于HTML的Web应用的最佳方式,咱们的博客引擎须要在Web浏览器中执行,若是可以在真实的浏览器中进行测试,这就是Play的Selenium测试所作的事情。框架
一个Selenium测试套件通常是个HTML文件。Selenium所使用的HTML语法有点使人乏味(就像是一个HTML表格元素同样)。好消息是,Play可让你使用模板引擎以及一些支持简化的Selenium语法的标签来生成它。这也意味着,你能够利用Play模板的表达能力(条件语句,循环语句等等)来生成许多复杂的测试。
固然,若是须要的话,你也能够不用模板的生成功能,直接撰写Selenium页面。其实,使用Selenium专用工具,好比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}
在yabe上运行这个测试应该不会出现任何问题。它只是打开主页,而后检测页面中是否包括"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}
还须要检查另外一个重要的事情,就是在每一个测试开始以前要有一个新的用户会话。这个会话将保持在浏览器的持久cookie中,若是不作清理,它会在两个连续的测试之间保持。
因此咱们的测试以前,先添加一个特殊的命令:
#{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}
而后运行它。啊,出错了;看来这里有点问题。
原来是验证码的问题。这个可模拟不了……只好做弊了嘿嘿。在测试模式中,咱们可须要把任何针对验证码的输入都当作正确的。而在测试模式下,框架的id是test
。因此修改掉yabe/app/controllers/Application.java
的postComment
action来跳过测试模式下的验证:
… if(!Play.id.equals("test")) { validation.equals(code, Cache.get(randomID)).message("Invalid code. Please type it again"); } …
如今修改下测试用例。验证码那一栏就随便乱写好了:
… type('author', 'Me') type('code', 'XXXXX') clickAndWait('css=input[type=submit]') …
而后从新运行测试,这下应该经过了。
固然咱们尚未完成应用所需的全部测试用例。可是对于咱们的教程来讲已经足够了。在现实的应用中,咱们怎么知道是否已经写了足够的测试用例?咱们须要一个代码覆盖率的概念。
Cobertura 模块使用Cobertura工具生成代码覆盖率报告。使用install
命令安装这个模块:
play install cobertura-{version}
咱们仅需在测试模式下启动这个模块。因此在application.conf
文件下添加这一行:
# Import the cobertura module in test mode %test.module.cobertura=${play.path}/modules/cobertura
如今在测试模式下从新启动应用,打开http://localhost:9000/@tests,选中全部测试并运行。应该是绿光满面的节奏。
在全部的测试结束后,中止应用,cobertura将生成代码覆盖率报告。接着你能够在浏览器打开yabe/test-result/code-coverage/index.html
,看一下那份报告。
若是从新启动应用,你也能够在http://localhost:9000/@cobertura看到它。
如你所见,咱们远远没有完成对应用的全面测试。一个好的测试覆盖率应该尽可能达到100%,即便检查全部的代码是几乎不可能的。毕竟咱们有时候不得不在测试模式下作一些变通,正如咱们曾对验证码作过的同样。