首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

精通 Grails 测试 Grails 应用程序(4)

精通 Grails 测试 Grails 应用程序(4)

创建和测试定制验证现在,应该处理下一个用户场景了。您需要确保 checkOut 日期发生在 checkIn 日期之后。要解决这个问题,您需要编写一个定制验证。编写完之后,要验证它。
将清单 11 中的定制验证代码添加到 static constraints 块:
清单 11. 一个定制的验证
1
2
3
4
5
6
7
8
9
10
11
class HotelStay {
  static constraints = {
    hotel(blank:false)
    checkIn()
    checkOut(validator:{val, obj->
      return val.after(obj.checkIn)
    })
  }
   
  //the rest of the class remains the same
}




val 变量是当前的字段。obj 变量表示当前的 HotelStay 实例。Groovy 将 before() 和 after() 方法添加到所有 Date 对象,所以这个验证仅返回 after() 方法调用的结果。如果 checkOut 发生在 checkIn 之后,验证返回 true。否则,它返回 false 并触发一个错误。
现在,输入 grails run-app。确保不能创建一个 checkOut 日期早于 checkIn 日期的新 HotelStay 实例。如图 10 所示:
图 10. 默认的定制验证错误消息打开 grails-app/i18n/messages.properties,并向 checkOut 字段添加一个定制验证消息:hotelStay.checkOut.validator.invalid=Sorry, you cannot check out before you check in。
保存 messages.properties 文件并尝试保存有缺陷的 HotelStay。您将看到如清单 11 所示的错误消息:
清单 11. 定制验证错误消息现在应该编写测试了,如清单 12 所示:
清单 12. 测试定制的验证
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.text.SimpleDateFormat
class HotelStayTests extends GroovyTestCase {
  void testCheckOutIsNotBeforeCheckIn(){
    def h = new HotelStay(hotel:"Radisson")
    def df = new SimpleDateFormat("MM/dd/yyyy")
    h.checkIn = df.parse("10/15/2008")
    h.checkOut = df.parse("10/10/2008")
   
    assertFalse "there should be errors", h.validate()
    def badField = h.errors.getFieldError('checkOut')
    assertNotNull "I'm expecting to find an error on the checkOut field", badField
    def code = badField?.codes.find {it == 'hotelStay.checkOut.validator.invalid'}
    assertNotNull "the checkOut field should be the culprit", code                 
  }
}




测试定制的 TagLib接下来是最后一个需要处理的用户场景。您已经在 create 和 edit 视图中成功地处理了 checkIn 和 checkOut 的时间戳部分,但它在 list 和 show 视图中仍然是错误的,如图 12 所示:
图 12. 默认的 Grails 日期输入(包括时间戳) 最简单的解决办法是定义一个新的 TagLib。您可以利用 Grails 已经定义的 <g:formatDate> 标记,但创建一个自己的定制标记也很容易。我想创建一个可以以两种方式使用的 <g:customDateFormat> 标记。
一种形式的 <g:customDateFormat> 标记打包一个 Date,并接受一个接受任何有效 SimpleDateFormat 模式的定制格式属性:
1
<g:customDateFormat format="EEEE">${new Date()}</g:customDateFormat>




因为大多数用例都以美国的 “MM/dd/yyyy” 格式返回日期,所以如果没有特别指定,我将采用这种格式:
1
<g:customDateFormat>${new Date()}</g:customDateFormat>




现在,您已经知道了每个用户场景的需求,那么请输入 grails create-tag-lib Date(如清单 13 所示),以创建一个全新的 DateTagLib.groovy 文件和一个相应的 DateTagLibTests.groovy 文件:
清单 13. 创建一个新的 TagLib
1
2
3
4
5
$ grails create-tag-lib Date
[copy] Copying 1 file to /src/trip-planner2/grails-app/taglib
Created TagLib for Date
[copy] Copying 1 file to /src/trip-planner2/test/integration
Created TagLibTests for Date




将清单 14 中的代码添加到 DateTagLib.groovy:
清单 14. 创建定制的 TagLib
1
2
3
4
5
6
7
8
9
10
11
12
import java.text.SimpleDateFormat

class DateTagLib {
  def customDateFormat = {attrs, body ->
    def b = attrs.body ?: body()
    def d = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").parse(b)
     
    //if no format attribute is supplied, use this
    def pattern = attrs["format"] ?: "MM/dd/yyyy"
    out << new SimpleDateFormat(pattern).format(d)
  }
}




TagLib 接受属性形式的简单的 String 值和标记体,并将一个 String 发送到输出流。由于您将使用这个定制标记封装未格式化的 Date 字段,所以需要两个 SimpleDateFormat 对象。输入对象读入一个与 Date.toString() 调用的默认格式相匹配的 String。当将其解析为适当的 Date 对象之后,您就可以创建第二个 SimpleDateFormat 对象,以便以另一种格式的 String 将它传回。
使用新的 TagLib 在 list.gsp 和 show.gsp 中封装 checkIn 和 checkOut 字段。如清单 15 所示:
清单 15. 使用定制的 TagLib
1
<g:customDateFormat>${fieldValue(bean:hotelStay, field:'checkIn')}</g:customDateFormat>




输入 grails run-app,然后访问 http://localhost:9090/trip/hotelStay/list,检查实际使用中的定制 TagLib,如图 13 所示:
图 13. 使用定制 TagLib 的数据输出现在,编写清单 16 中的几个测试,用来检查 TagLib 是否按照预期工作:
清单 16. 测试定制的 TagLib
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import java.text.SimpleDateFormat

class DateTagLibTests extends GroovyTestCase {
    void testNoFormat() {
      def output =
         new DateTagLib().customDateFormat(format:null, body:"2008-10-01 00:00:00.0")
      println "\ncustomDateFormat using the default format:"
      println output
      
      assertEquals "was the default format used?", "10/01/2008", output
    }

    void testCustomFormat() {
      def output =
         new DateTagLib().customDateFormat(format:"EEEE", body:"2008-10-01 00:00:00.0")
      assertEquals "was the custom format used?", "Wednesday", output
    }
}

返回列表